NCDUdevCache.c 12 KB

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