NCDUdevMonitorParser.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /**
  2. * @file NCDUdevMonitorParser.c
  3. * @author Ambroz Bizjak <ambrop7@gmail.com>
  4. *
  5. * @section LICENSE
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the author nor the
  15. * names of its contributors may be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include <misc/string_begins_with.h>
  32. #include <misc/balloc.h>
  33. #include <base/BLog.h>
  34. #include <udevmonitor/NCDUdevMonitorParser.h>
  35. #include <generated/blog_channel_NCDUdevMonitorParser.h>
  36. #define PROPERTY_REGEX "^([^=]+)=(.*)$"
  37. static uint8_t * find_end (uint8_t *buf, size_t len)
  38. {
  39. while (len >= 2) {
  40. if (buf[0] == '\n' && buf[1] == '\n') {
  41. return (buf + 2);
  42. }
  43. buf++;
  44. len--;
  45. }
  46. return NULL;
  47. }
  48. static int parse_property (NCDUdevMonitorParser *o, char *data)
  49. {
  50. ASSERT(o->ready_num_properties >= 0)
  51. ASSERT(o->ready_num_properties <= o->max_properties)
  52. if (o->ready_num_properties == o->max_properties) {
  53. BLog(BLOG_ERROR, "too many properties");
  54. return 0;
  55. }
  56. struct NCDUdevMonitorParser_property *prop = &o->ready_properties[o->ready_num_properties];
  57. // execute property regex
  58. regmatch_t matches[3];
  59. if (regexec(&o->property_regex, data, 3, matches, 0) != 0) {
  60. BLog(BLOG_ERROR, "failed to parse property");
  61. return 0;
  62. }
  63. // extract components
  64. prop->name = data + matches[1].rm_so;
  65. *(data + matches[1].rm_eo) = '\0';
  66. prop->value = data + matches[2].rm_so;
  67. *(data + matches[2].rm_eo) = '\0';
  68. // register property
  69. o->ready_num_properties++;
  70. return 1;
  71. }
  72. static int parse_message (NCDUdevMonitorParser *o)
  73. {
  74. ASSERT(!o->is_ready)
  75. ASSERT(o->ready_len >= 2)
  76. ASSERT(o->buf[o->ready_len - 2] == '\n')
  77. ASSERT(o->buf[o->ready_len - 1] == '\n')
  78. // zero terminate message (replacing the second newline)
  79. o->buf[o->ready_len - 1] = '\0';
  80. // start parsing
  81. char *line = (char *)o->buf;
  82. int first_line = 1;
  83. // set is not ready event
  84. o->ready_is_ready_event = 0;
  85. // init properties
  86. o->ready_num_properties = 0;
  87. do {
  88. // find end of line
  89. char *line_end = strchr(line, '\n');
  90. ASSERT(line_end)
  91. // zero terminate line
  92. *line_end = '\0';
  93. if (o->is_info_mode) {
  94. // ignore W: entries with missing space
  95. if (string_begins_with(line, "W:")) {
  96. goto nextline;
  97. }
  98. // parse prefix
  99. if (strlen(line) < 3 || line[1] != ':' || line[2] != ' ') {
  100. BLog(BLOG_ERROR, "failed to parse head");
  101. return 0;
  102. }
  103. char line_type = line[0];
  104. char *line_value = line + 3;
  105. if (first_line) {
  106. if (line_type != 'P') {
  107. BLog(BLOG_ERROR, "wrong first line type");
  108. return 0;
  109. }
  110. } else {
  111. if (line_type == 'E') {
  112. if (!parse_property(o, line_value)) {
  113. return 0;
  114. }
  115. }
  116. }
  117. } else {
  118. if (first_line) {
  119. // is this the initial informational message?
  120. if (string_begins_with(line, "monitor")) {
  121. o->ready_is_ready_event = 1;
  122. break;
  123. }
  124. // check first line
  125. if (!string_begins_with(line, "UDEV ") && !string_begins_with(line, "KERNEL")) {
  126. BLog(BLOG_ERROR, "failed to parse head");
  127. return 0;
  128. }
  129. } else {
  130. if (!parse_property(o, line)) {
  131. return 0;
  132. }
  133. }
  134. }
  135. nextline:
  136. first_line = 0;
  137. line = line_end + 1;
  138. } while (*line);
  139. // set ready
  140. o->is_ready = 1;
  141. return 1;
  142. }
  143. static void process_data (NCDUdevMonitorParser *o)
  144. {
  145. ASSERT(!o->is_ready)
  146. while (1) {
  147. // look for end of event
  148. uint8_t *c = find_end(o->buf, o->buf_used);
  149. if (!c) {
  150. // check for out of buffer condition
  151. if (o->buf_used == o->buf_size) {
  152. BLog(BLOG_ERROR, "out of buffer");
  153. o->buf_used = 0;
  154. }
  155. // receive more data
  156. StreamRecvInterface_Receiver_Recv(o->input, o->buf + o->buf_used, o->buf_size - o->buf_used);
  157. return;
  158. }
  159. // remember message length
  160. o->ready_len = c - o->buf;
  161. // parse message
  162. if (parse_message(o)) {
  163. break;
  164. }
  165. // shift buffer
  166. memmove(o->buf, o->buf + o->ready_len, o->buf_used - o->ready_len);
  167. o->buf_used -= o->ready_len;
  168. }
  169. // call handler
  170. o->handler(o->user);
  171. return;
  172. }
  173. static void input_handler_done (NCDUdevMonitorParser *o, int data_len)
  174. {
  175. DebugObject_Access(&o->d_obj);
  176. ASSERT(!o->is_ready)
  177. ASSERT(data_len > 0)
  178. ASSERT(data_len <= o->buf_size - o->buf_used)
  179. // increment buffer position
  180. o->buf_used += data_len;
  181. // process data
  182. process_data(o);
  183. return;
  184. }
  185. static void done_job_handler (NCDUdevMonitorParser *o)
  186. {
  187. DebugObject_Access(&o->d_obj);
  188. ASSERT(o->is_ready)
  189. // shift buffer
  190. memmove(o->buf, o->buf + o->ready_len, o->buf_used - o->ready_len);
  191. o->buf_used -= o->ready_len;
  192. // set not ready
  193. o->is_ready = 0;
  194. // process data
  195. process_data(o);
  196. return;
  197. }
  198. int NCDUdevMonitorParser_Init (NCDUdevMonitorParser *o, StreamRecvInterface *input, int buf_size, int max_properties,
  199. int is_info_mode, BPendingGroup *pg, void *user,
  200. NCDUdevMonitorParser_handler handler)
  201. {
  202. ASSERT(buf_size > 0)
  203. ASSERT(max_properties >= 0)
  204. ASSERT(is_info_mode == 0 || is_info_mode == 1)
  205. // init arguments
  206. o->input = input;
  207. o->buf_size = buf_size;
  208. o->max_properties = max_properties;
  209. o->is_info_mode = is_info_mode;
  210. o->user = user;
  211. o->handler = handler;
  212. // init input
  213. StreamRecvInterface_Receiver_Init(o->input, (StreamRecvInterface_handler_done)input_handler_done, o);
  214. // init property regex
  215. if (regcomp(&o->property_regex, PROPERTY_REGEX, REG_EXTENDED) != 0) {
  216. BLog(BLOG_ERROR, "regcomp failed");
  217. goto fail1;
  218. }
  219. // init done job
  220. BPending_Init(&o->done_job, pg, (BPending_handler)done_job_handler, o);
  221. // allocate buffer
  222. if (!(o->buf = malloc(o->buf_size))) {
  223. BLog(BLOG_ERROR, "malloc failed");
  224. goto fail2;
  225. }
  226. // set buffer position
  227. o->buf_used = 0;
  228. // set not ready
  229. o->is_ready = 0;
  230. // allocate properties
  231. if (!(o->ready_properties = BAllocArray(o->max_properties, sizeof(o->ready_properties[0])))) {
  232. BLog(BLOG_ERROR, "BAllocArray failed");
  233. goto fail3;
  234. }
  235. // start receiving
  236. StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_size);
  237. DebugObject_Init(&o->d_obj);
  238. return 1;
  239. fail3:
  240. free(o->buf);
  241. fail2:
  242. BPending_Free(&o->done_job);
  243. regfree(&o->property_regex);
  244. fail1:
  245. return 0;
  246. }
  247. void NCDUdevMonitorParser_Free (NCDUdevMonitorParser *o)
  248. {
  249. DebugObject_Free(&o->d_obj);
  250. // free properties
  251. BFree(o->ready_properties);
  252. // free buffer
  253. free(o->buf);
  254. // free done job
  255. BPending_Free(&o->done_job);
  256. // free property regex
  257. regfree(&o->property_regex);
  258. }
  259. void NCDUdevMonitorParser_AssertReady (NCDUdevMonitorParser *o)
  260. {
  261. DebugObject_Access(&o->d_obj);
  262. ASSERT(o->is_ready)
  263. }
  264. void NCDUdevMonitorParser_Done (NCDUdevMonitorParser *o)
  265. {
  266. DebugObject_Access(&o->d_obj);
  267. ASSERT(o->is_ready)
  268. // schedule done job
  269. BPending_Set(&o->done_job);
  270. }
  271. int NCDUdevMonitorParser_IsReadyEvent (NCDUdevMonitorParser *o)
  272. {
  273. DebugObject_Access(&o->d_obj);
  274. ASSERT(o->is_ready)
  275. return o->ready_is_ready_event;
  276. }
  277. int NCDUdevMonitorParser_GetNumProperties (NCDUdevMonitorParser *o)
  278. {
  279. DebugObject_Access(&o->d_obj);
  280. ASSERT(o->is_ready)
  281. return o->ready_num_properties;
  282. }
  283. void NCDUdevMonitorParser_GetProperty (NCDUdevMonitorParser *o, int index, const char **name, const char **value)
  284. {
  285. DebugObject_Access(&o->d_obj);
  286. ASSERT(o->is_ready)
  287. ASSERT(index >= 0)
  288. ASSERT(index < o->ready_num_properties)
  289. *name = o->ready_properties[index].name;
  290. *value = o->ready_properties[index].value;
  291. }