NCDUdevCache.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /**
  2. * @file NCDUdevCache.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/offset.h>
  32. #include <misc/string_begins_with.h>
  33. #include <misc/concat_strings.h>
  34. #include <base/BLog.h>
  35. #include <udevmonitor/NCDUdevCache.h>
  36. #include <generated/blog_channel_NCDUdevCache.h>
  37. static int string_comparator (void *unused, const char **str1, const char **str2)
  38. {
  39. int c = strcmp(*str1, *str2);
  40. if (c < 0) {
  41. return -1;
  42. }
  43. if (c > 0) {
  44. return 1;
  45. }
  46. return 0;
  47. }
  48. static void free_device (NCDUdevCache *o, struct NCDUdevCache_device *device)
  49. {
  50. if (device->is_cleaned) {
  51. // remove from cleaned devices list
  52. LinkedList1_Remove(&o->cleaned_devices_list, &device->cleaned_devices_list_node);
  53. } else {
  54. // remove from devices tree
  55. BAVL_Remove(&o->devices_tree, &device->devices_tree_node);
  56. }
  57. // free map
  58. BStringMap_Free(&device->map);
  59. // free structure
  60. free(device);
  61. }
  62. static struct NCDUdevCache_device * lookup_device (NCDUdevCache *o, const char *devpath)
  63. {
  64. BAVLNode *tree_node = BAVL_LookupExact(&o->devices_tree, &devpath);
  65. if (!tree_node) {
  66. return NULL;
  67. }
  68. struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node);
  69. ASSERT(!device->is_cleaned)
  70. return device;
  71. }
  72. static void rename_devices (NCDUdevCache *o, const char *prefix, const char *new_prefix)
  73. {
  74. ASSERT(strlen(prefix) > 0)
  75. size_t prefix_len = strlen(prefix);
  76. // lookup prefix
  77. BAVLNode *tree_node = BAVL_Lookup(&o->devices_tree, &prefix);
  78. if (!tree_node) {
  79. return;
  80. }
  81. struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node);
  82. ASSERT(!device->is_cleaned)
  83. // if the result does not begin with prefix, we might gave gotten the device before all
  84. // devices beginning with prefix, so skip it
  85. if (!string_begins_with(device->devpath, prefix)) {
  86. tree_node = BAVL_GetNext(&o->devices_tree, tree_node);
  87. }
  88. while (tree_node) {
  89. // get next node (must be here because we rename this device)
  90. BAVLNode *next_tree_node = BAVL_GetNext(&o->devices_tree, tree_node);
  91. // get device
  92. device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node);
  93. ASSERT(!device->is_cleaned)
  94. // if it doesn't begin with prefix, we're done
  95. if (!string_begins_with(device->devpath, prefix)) {
  96. break;
  97. }
  98. // build new devpath
  99. char *new_devpath = concat_strings(2, new_prefix, device->devpath + prefix_len);
  100. if (!new_devpath) {
  101. BLog(BLOG_ERROR, "concat_strings failed");
  102. goto fail_loop0;
  103. }
  104. // make sure the new name does not exist
  105. if (BAVL_LookupExact(&o->devices_tree, &new_devpath)) {
  106. BLog(BLOG_ERROR, "rename destination already exists");
  107. goto fail_loop1;
  108. }
  109. BLog(BLOG_DEBUG, "rename %s -> %s", device->devpath, new_devpath);
  110. // remove from tree
  111. BAVL_Remove(&o->devices_tree, &device->devices_tree_node);
  112. // update devpath in map
  113. if (!BStringMap_Set(&device->map, "DEVPATH", new_devpath)) {
  114. BLog(BLOG_ERROR, "BStringMap_Set failed");
  115. ASSERT_EXECUTE(BAVL_Insert(&o->devices_tree, &device->devices_tree_node, NULL))
  116. goto fail_loop1;
  117. }
  118. // update devpath pointer
  119. device->devpath = BStringMap_Get(&device->map, "DEVPATH");
  120. ASSERT(device->devpath)
  121. // insert to tree
  122. ASSERT_EXECUTE(BAVL_Insert(&o->devices_tree, &device->devices_tree_node, NULL))
  123. fail_loop1:
  124. free(new_devpath);
  125. fail_loop0:
  126. tree_node = next_tree_node;
  127. }
  128. }
  129. static int add_device (NCDUdevCache *o, BStringMap map)
  130. {
  131. ASSERT(BStringMap_Get(&map, "DEVPATH"))
  132. // alloc structure
  133. struct NCDUdevCache_device *device = malloc(sizeof(*device));
  134. if (!device) {
  135. BLog(BLOG_ERROR, "malloc failed");
  136. goto fail0;
  137. }
  138. // init map
  139. device->map = map;
  140. // set device path
  141. device->devpath = BStringMap_Get(&device->map, "DEVPATH");
  142. // insert to devices tree
  143. BAVLNode *ex_node;
  144. if (!BAVL_Insert(&o->devices_tree, &device->devices_tree_node, &ex_node)) {
  145. BLog(BLOG_DEBUG, "update %s", device->devpath);
  146. // get existing device
  147. struct NCDUdevCache_device *ex_device = UPPER_OBJECT(ex_node, struct NCDUdevCache_device, devices_tree_node);
  148. ASSERT(!ex_device->is_cleaned)
  149. // remove exiting device
  150. free_device(o, ex_device);
  151. // insert
  152. ASSERT_EXECUTE(BAVL_Insert(&o->devices_tree, &device->devices_tree_node, NULL))
  153. } else {
  154. BLog(BLOG_DEBUG, "add %s", device->devpath);
  155. }
  156. // set not cleaned
  157. device->is_cleaned = 0;
  158. // set refreshed
  159. device->is_refreshed = 1;
  160. return 1;
  161. fail0:
  162. return 0;
  163. }
  164. void NCDUdevCache_Init (NCDUdevCache *o)
  165. {
  166. // init devices tree
  167. BAVL_Init(&o->devices_tree, OFFSET_DIFF(struct NCDUdevCache_device, devpath, devices_tree_node), (BAVL_comparator)string_comparator, NULL);
  168. // init cleaned devices list
  169. LinkedList1_Init(&o->cleaned_devices_list);
  170. DebugObject_Init(&o->d_obj);
  171. }
  172. void NCDUdevCache_Free (NCDUdevCache *o)
  173. {
  174. DebugObject_Free(&o->d_obj);
  175. // free cleaned devices
  176. LinkedList1Node *list_node;
  177. while (list_node = LinkedList1_GetFirst(&o->cleaned_devices_list)) {
  178. struct NCDUdevCache_device *device = UPPER_OBJECT(list_node, struct NCDUdevCache_device, cleaned_devices_list_node);
  179. ASSERT(device->is_cleaned)
  180. free_device(o, device);
  181. }
  182. // free devices
  183. BAVLNode *tree_node;
  184. while (tree_node = BAVL_GetFirst(&o->devices_tree)) {
  185. struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node);
  186. ASSERT(!device->is_cleaned)
  187. free_device(o, device);
  188. }
  189. }
  190. const BStringMap * NCDUdevCache_Query (NCDUdevCache *o, const char *devpath)
  191. {
  192. DebugObject_Access(&o->d_obj);
  193. // lookup device
  194. struct NCDUdevCache_device *device = lookup_device(o, devpath);
  195. if (!device) {
  196. return NULL;
  197. }
  198. // return map
  199. return &device->map;
  200. }
  201. int NCDUdevCache_Event (NCDUdevCache *o, BStringMap map)
  202. {
  203. DebugObject_Access(&o->d_obj);
  204. // get device path
  205. const char *devpath = BStringMap_Get(&map, "DEVPATH");
  206. if (!devpath) {
  207. BLog(BLOG_ERROR, "missing DEVPATH");
  208. goto fail;
  209. }
  210. // get action
  211. const char *action = BStringMap_Get(&map, "ACTION");
  212. // if this is a remove event, remove device if we have it
  213. if (action && !strcmp(action, "remove")) {
  214. // remove existing device
  215. struct NCDUdevCache_device *device = lookup_device(o, devpath);
  216. if (device) {
  217. BLog(BLOG_DEBUG, "remove %s", devpath);
  218. free_device(o, device);
  219. } else {
  220. BLog(BLOG_DEBUG, "remove unknown %s", devpath);
  221. }
  222. // eat map
  223. BStringMap_Free(&map);
  224. return 1;
  225. }
  226. // if this is a move event, remove old device and contaned devices
  227. if (action && !strcmp(action, "move")) {
  228. const char *devpath_old = BStringMap_Get(&map, "DEVPATH_OLD");
  229. if (!devpath_old) {
  230. goto fail_rename0;
  231. }
  232. // remove old device
  233. struct NCDUdevCache_device *old_device = lookup_device(o, devpath_old);
  234. if (old_device) {
  235. BLog(BLOG_DEBUG, "remove moved %s", old_device->devpath);
  236. free_device(o, old_device);
  237. }
  238. // construct prefix "<devpath_old>/" and new prefix "<devpath>/"
  239. char *prefix = concat_strings(2, devpath_old, "/");
  240. if (!prefix) {
  241. BLog(BLOG_ERROR, "concat_strings failed");
  242. goto fail_rename0;;
  243. }
  244. char *new_prefix = concat_strings(2, devpath, "/");
  245. if (!new_prefix) {
  246. BLog(BLOG_ERROR, "concat_strings failed");
  247. goto fail_rename1;
  248. }
  249. // rename devices with paths starting with prefix
  250. rename_devices(o, prefix, new_prefix);
  251. free(new_prefix);
  252. fail_rename1:
  253. free(prefix);
  254. fail_rename0:;
  255. }
  256. // add device
  257. if (!add_device(o, map)) {
  258. BLog(BLOG_DEBUG, "failed to add device %s", devpath);
  259. goto fail;
  260. }
  261. return 1;
  262. fail:
  263. return 0;
  264. }
  265. void NCDUdevCache_StartClean (NCDUdevCache *o)
  266. {
  267. DebugObject_Access(&o->d_obj);
  268. // mark all devices not refreshed
  269. BAVLNode *tree_node = BAVL_GetFirst(&o->devices_tree);
  270. while (tree_node) {
  271. struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node);
  272. ASSERT(!device->is_cleaned)
  273. // set device not refreshed
  274. device->is_refreshed = 0;
  275. tree_node = BAVL_GetNext(&o->devices_tree, tree_node);
  276. }
  277. }
  278. void NCDUdevCache_FinishClean (NCDUdevCache *o)
  279. {
  280. DebugObject_Access(&o->d_obj);
  281. // move all devices not marked refreshed to the cleaned devices list
  282. BAVLNode *tree_node = BAVL_GetFirst(&o->devices_tree);
  283. while (tree_node) {
  284. BAVLNode *next_tree_node = BAVL_GetNext(&o->devices_tree, tree_node);
  285. struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node);
  286. ASSERT(!device->is_cleaned)
  287. if (!device->is_refreshed) {
  288. BLog(BLOG_DEBUG, "clean %s", device->devpath);
  289. // remove from devices tree
  290. BAVL_Remove(&o->devices_tree, &device->devices_tree_node);
  291. // insert to cleaned devices list
  292. LinkedList1_Append(&o->cleaned_devices_list, &device->cleaned_devices_list_node);
  293. // set device cleaned
  294. device->is_cleaned = 1;
  295. }
  296. tree_node = next_tree_node;
  297. }
  298. }
  299. int NCDUdevCache_GetCleanedDevice (NCDUdevCache *o, BStringMap *out_map)
  300. {
  301. DebugObject_Access(&o->d_obj);
  302. // get cleaned device
  303. LinkedList1Node *list_node = LinkedList1_GetFirst(&o->cleaned_devices_list);
  304. if (!list_node) {
  305. return 0;
  306. }
  307. struct NCDUdevCache_device *device = UPPER_OBJECT(list_node, struct NCDUdevCache_device, cleaned_devices_list_node);
  308. ASSERT(device->is_cleaned)
  309. // remove from cleaned devices list
  310. LinkedList1_Remove(&o->cleaned_devices_list, &device->cleaned_devices_list_node);
  311. // give away map
  312. *out_map = device->map;
  313. // free structure
  314. free(device);
  315. return 1;
  316. }
  317. const char * NCDUdevCache_First (NCDUdevCache *o)
  318. {
  319. DebugObject_Access(&o->d_obj);
  320. BAVLNode *tree_node = BAVL_GetFirst(&o->devices_tree);
  321. if (!tree_node) {
  322. return NULL;
  323. }
  324. struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node);
  325. ASSERT(!device->is_cleaned)
  326. return device->devpath;
  327. }
  328. const char * NCDUdevCache_Next (NCDUdevCache *o, const char *key)
  329. {
  330. ASSERT(BAVL_LookupExact(&o->devices_tree, &key))
  331. BAVLNode *tree_node = BAVL_GetNext(&o->devices_tree, BAVL_LookupExact(&o->devices_tree, &key));
  332. if (!tree_node) {
  333. return NULL;
  334. }
  335. struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node);
  336. ASSERT(!device->is_cleaned)
  337. return device->devpath;
  338. }