NCDUdevMonitorParser.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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. // parse prefix
  95. if (strlen(line) < 3 || line[1] != ':' || line[2] != ' ') {
  96. BLog(BLOG_ERROR, "failed to parse head");
  97. return 0;
  98. }
  99. char line_type = line[0];
  100. char *line_value = line + 3;
  101. if (first_line) {
  102. if (line_type != 'P') {
  103. BLog(BLOG_ERROR, "wrong first line type");
  104. return 0;
  105. }
  106. } else {
  107. if (line_type == 'E') {
  108. if (!parse_property(o, line_value)) {
  109. return 0;
  110. }
  111. }
  112. }
  113. } else {
  114. if (first_line) {
  115. // is this the initial informational message?
  116. if (string_begins_with(line, "monitor")) {
  117. o->ready_is_ready_event = 1;
  118. break;
  119. }
  120. // check first line
  121. if (!string_begins_with(line, "UDEV ")) {
  122. BLog(BLOG_ERROR, "failed to parse head");
  123. return 0;
  124. }
  125. } else {
  126. if (!parse_property(o, line)) {
  127. return 0;
  128. }
  129. }
  130. }
  131. first_line = 0;
  132. line = line_end + 1;
  133. } while (*line);
  134. // set ready
  135. o->is_ready = 1;
  136. return 1;
  137. }
  138. static void process_data (NCDUdevMonitorParser *o)
  139. {
  140. ASSERT(!o->is_ready)
  141. while (1) {
  142. // look for end of event
  143. uint8_t *c = find_end(o->buf, o->buf_used);
  144. if (!c) {
  145. // check for out of buffer condition
  146. if (o->buf_used == o->buf_size) {
  147. BLog(BLOG_ERROR, "out of buffer");
  148. o->buf_used = 0;
  149. }
  150. // receive more data
  151. StreamRecvInterface_Receiver_Recv(o->input, o->buf + o->buf_used, o->buf_size - o->buf_used);
  152. return;
  153. }
  154. // remember message length
  155. o->ready_len = c - o->buf;
  156. // parse message
  157. if (parse_message(o)) {
  158. break;
  159. }
  160. // shift buffer
  161. memmove(o->buf, o->buf + o->ready_len, o->buf_used - o->ready_len);
  162. o->buf_used -= o->ready_len;
  163. }
  164. // call handler
  165. o->handler(o->user);
  166. return;
  167. }
  168. static void input_handler_done (NCDUdevMonitorParser *o, int data_len)
  169. {
  170. DebugObject_Access(&o->d_obj);
  171. ASSERT(!o->is_ready)
  172. ASSERT(data_len > 0)
  173. ASSERT(data_len <= o->buf_size - o->buf_used)
  174. // increment buffer position
  175. o->buf_used += data_len;
  176. // process data
  177. process_data(o);
  178. return;
  179. }
  180. static void done_job_handler (NCDUdevMonitorParser *o)
  181. {
  182. DebugObject_Access(&o->d_obj);
  183. ASSERT(o->is_ready)
  184. // shift buffer
  185. memmove(o->buf, o->buf + o->ready_len, o->buf_used - o->ready_len);
  186. o->buf_used -= o->ready_len;
  187. // set not ready
  188. o->is_ready = 0;
  189. // process data
  190. process_data(o);
  191. return;
  192. }
  193. int NCDUdevMonitorParser_Init (NCDUdevMonitorParser *o, StreamRecvInterface *input, int buf_size, int max_properties,
  194. int is_info_mode, BPendingGroup *pg, void *user,
  195. NCDUdevMonitorParser_handler handler)
  196. {
  197. ASSERT(buf_size > 0)
  198. ASSERT(max_properties >= 0)
  199. ASSERT(is_info_mode == 0 || is_info_mode == 1)
  200. // init arguments
  201. o->input = input;
  202. o->buf_size = buf_size;
  203. o->max_properties = max_properties;
  204. o->is_info_mode = is_info_mode;
  205. o->user = user;
  206. o->handler = handler;
  207. // init input
  208. StreamRecvInterface_Receiver_Init(o->input, (StreamRecvInterface_handler_done)input_handler_done, o);
  209. // init property regex
  210. if (regcomp(&o->property_regex, PROPERTY_REGEX, REG_EXTENDED) != 0) {
  211. BLog(BLOG_ERROR, "regcomp failed");
  212. goto fail1;
  213. }
  214. // init done job
  215. BPending_Init(&o->done_job, pg, (BPending_handler)done_job_handler, o);
  216. // allocate buffer
  217. if (!(o->buf = malloc(o->buf_size))) {
  218. BLog(BLOG_ERROR, "malloc failed");
  219. goto fail2;
  220. }
  221. // set buffer position
  222. o->buf_used = 0;
  223. // set not ready
  224. o->is_ready = 0;
  225. // allocate properties
  226. if (!(o->ready_properties = BAllocArray(o->max_properties, sizeof(o->ready_properties[0])))) {
  227. BLog(BLOG_ERROR, "BAllocArray failed");
  228. goto fail3;
  229. }
  230. // start receiving
  231. StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_size);
  232. DebugObject_Init(&o->d_obj);
  233. return 1;
  234. fail3:
  235. free(o->buf);
  236. fail2:
  237. BPending_Free(&o->done_job);
  238. regfree(&o->property_regex);
  239. fail1:
  240. return 0;
  241. }
  242. void NCDUdevMonitorParser_Free (NCDUdevMonitorParser *o)
  243. {
  244. DebugObject_Free(&o->d_obj);
  245. // free properties
  246. BFree(o->ready_properties);
  247. // free buffer
  248. free(o->buf);
  249. // free done job
  250. BPending_Free(&o->done_job);
  251. // free property regex
  252. regfree(&o->property_regex);
  253. }
  254. void NCDUdevMonitorParser_AssertReady (NCDUdevMonitorParser *o)
  255. {
  256. DebugObject_Access(&o->d_obj);
  257. ASSERT(o->is_ready)
  258. }
  259. void NCDUdevMonitorParser_Done (NCDUdevMonitorParser *o)
  260. {
  261. DebugObject_Access(&o->d_obj);
  262. ASSERT(o->is_ready)
  263. // schedule done job
  264. BPending_Set(&o->done_job);
  265. }
  266. int NCDUdevMonitorParser_IsReadyEvent (NCDUdevMonitorParser *o)
  267. {
  268. DebugObject_Access(&o->d_obj);
  269. ASSERT(o->is_ready)
  270. return o->ready_is_ready_event;
  271. }
  272. int NCDUdevMonitorParser_GetNumProperties (NCDUdevMonitorParser *o)
  273. {
  274. DebugObject_Access(&o->d_obj);
  275. ASSERT(o->is_ready)
  276. return o->ready_num_properties;
  277. }
  278. void NCDUdevMonitorParser_GetProperty (NCDUdevMonitorParser *o, int index, const char **name, const char **value)
  279. {
  280. DebugObject_Access(&o->d_obj);
  281. ASSERT(o->is_ready)
  282. ASSERT(index >= 0)
  283. ASSERT(index < o->ready_num_properties)
  284. *name = o->ready_properties[index].name;
  285. *value = o->ready_properties[index].value;
  286. }