CGroupUtils.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. /*
  2. htop - CGroupUtils.c
  3. (C) 2021 htop dev team
  4. Released under the GNU GPLv2+, see the COPYING file
  5. in the source distribution for its full text.
  6. */
  7. #include "config.h" // IWYU pragma: keep
  8. #include "linux/CGroupUtils.h"
  9. #include <stdbool.h>
  10. #include <stddef.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include "Macros.h"
  14. #include "XUtils.h"
  15. static const char* str_slice_suffix = ".slice";
  16. static const char* str_system_slice = "system.slice";
  17. static const char* str_user_slice = "user.slice";
  18. static const char* str_machine_slice = "machine.slice";
  19. static const char* str_user_slice_prefix = "/user-";
  20. static const char* str_system_slice_prefix = "/system-";
  21. static const char* str_lxc_monitor_legacy = "lxc.monitor";
  22. static const char* str_lxc_payload_legacy = "lxc.payload";
  23. static const char* str_lxc_monitor_prefix = "lxc.monitor.";
  24. static const char* str_lxc_payload_prefix = "lxc.payload.";
  25. static const char* str_nspawn_scope_prefix = "machine-";
  26. static const char* str_nspawn_monitor_label = "/supervisor";
  27. static const char* str_nspawn_payload_label = "/payload";
  28. static const char* str_snap_scope_prefix = "snap.";
  29. static const char* str_pod_scope_prefix = "libpod-";
  30. static const char* str_docker_scope_prefix = "docker-";
  31. static const char* str_service_suffix = ".service";
  32. static const char* str_scope_suffix = ".scope";
  33. typedef struct StrBuf_state {
  34. char* buf;
  35. size_t size;
  36. size_t pos;
  37. } StrBuf_state;
  38. typedef bool (*StrBuf_putc_t)(StrBuf_state* p, char c);
  39. static bool StrBuf_putc_count(StrBuf_state* p, ATTR_UNUSED char c) {
  40. p->pos++;
  41. return true;
  42. }
  43. static bool StrBuf_putc_write(StrBuf_state* p, char c) {
  44. if (p->pos >= p->size)
  45. return false;
  46. p->buf[p->pos] = c;
  47. p->pos++;
  48. return true;
  49. }
  50. static bool StrBuf_putsn(StrBuf_state* p, StrBuf_putc_t w, const char* s, size_t count) {
  51. for (; count; count--)
  52. if (!w(p, *s++))
  53. return false;
  54. return true;
  55. }
  56. static bool StrBuf_putsz(StrBuf_state* p, StrBuf_putc_t w, const char* s) {
  57. while (*s)
  58. if (!w(p, *s++))
  59. return false;
  60. return true;
  61. }
  62. static bool Label_checkEqual(const char* labelStart, size_t labelLen, const char* expected) {
  63. return labelLen == strlen(expected) && String_startsWith(labelStart, expected);
  64. }
  65. static bool Label_checkPrefix(const char* labelStart, size_t labelLen, const char* expected) {
  66. return labelLen > strlen(expected) && String_startsWith(labelStart, expected);
  67. }
  68. static bool Label_checkSuffix(const char* labelStart, size_t labelLen, const char* expected) {
  69. return labelLen > strlen(expected) && String_startsWith(labelStart + labelLen - strlen(expected), expected);
  70. }
  71. static bool CGroup_filterName_internal(const char* cgroup, StrBuf_state* s, StrBuf_putc_t w) {
  72. while (*cgroup) {
  73. if ('/' == *cgroup) {
  74. while ('/' == *cgroup)
  75. cgroup++;
  76. if (!w(s, '/'))
  77. return false;
  78. continue;
  79. }
  80. const char* labelStart = cgroup;
  81. const char* nextSlash = String_strchrnul(labelStart, '/');
  82. const size_t labelLen = nextSlash - labelStart;
  83. if (Label_checkEqual(labelStart, labelLen, str_system_slice)) {
  84. cgroup = nextSlash;
  85. if (!StrBuf_putsz(s, w, "[S]"))
  86. return false;
  87. if (String_startsWith(cgroup, str_system_slice_prefix)) {
  88. cgroup = String_strchrnul(cgroup + 1, '/');
  89. continue;
  90. }
  91. continue;
  92. }
  93. if (Label_checkEqual(labelStart, labelLen, str_machine_slice)) {
  94. cgroup = nextSlash;
  95. if (!StrBuf_putsz(s, w, "[M]"))
  96. return false;
  97. continue;
  98. }
  99. if (Label_checkEqual(labelStart, labelLen, str_user_slice)) {
  100. cgroup = nextSlash;
  101. if (!StrBuf_putsz(s, w, "[U]"))
  102. return false;
  103. if (!String_startsWith(cgroup, str_user_slice_prefix))
  104. continue;
  105. const char* userSliceSlash = String_strchrnul(cgroup + strlen(str_user_slice_prefix), '/');
  106. const char* sliceSpec = userSliceSlash - strlen(str_slice_suffix);
  107. if (!String_startsWith(sliceSpec, str_slice_suffix))
  108. continue;
  109. const size_t sliceNameLen = sliceSpec - (cgroup + strlen(str_user_slice_prefix));
  110. s->pos--;
  111. if (!w(s, ':'))
  112. return false;
  113. if (!StrBuf_putsn(s, w, cgroup + strlen(str_user_slice_prefix), sliceNameLen))
  114. return false;
  115. if (!w(s, ']'))
  116. return false;
  117. cgroup = userSliceSlash;
  118. continue;
  119. }
  120. if (Label_checkSuffix(labelStart, labelLen, str_slice_suffix)) {
  121. const size_t sliceNameLen = labelLen - strlen(str_slice_suffix);
  122. if (!w(s, '['))
  123. return false;
  124. if (!StrBuf_putsn(s, w, cgroup, sliceNameLen))
  125. return false;
  126. if (!w(s, ']'))
  127. return false;
  128. cgroup = nextSlash;
  129. continue;
  130. }
  131. if (Label_checkPrefix(labelStart, labelLen, str_lxc_payload_prefix)) {
  132. const size_t cgroupNameLen = labelLen - strlen(str_lxc_payload_prefix);
  133. if (!StrBuf_putsz(s, w, "[lxc:"))
  134. return false;
  135. if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_payload_prefix), cgroupNameLen))
  136. return false;
  137. if (!w(s, ']'))
  138. return false;
  139. cgroup = nextSlash;
  140. continue;
  141. }
  142. if (Label_checkPrefix(labelStart, labelLen, str_lxc_monitor_prefix)) {
  143. const size_t cgroupNameLen = labelLen - strlen(str_lxc_monitor_prefix);
  144. if (!StrBuf_putsz(s, w, "[LXC:"))
  145. return false;
  146. if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_monitor_prefix), cgroupNameLen))
  147. return false;
  148. if (!w(s, ']'))
  149. return false;
  150. cgroup = nextSlash;
  151. continue;
  152. }
  153. // LXC legacy cgroup naming
  154. if (Label_checkEqual(labelStart, labelLen, str_lxc_monitor_legacy) ||
  155. Label_checkEqual(labelStart, labelLen, str_lxc_payload_legacy)) {
  156. bool isMonitor = Label_checkEqual(labelStart, labelLen, str_lxc_monitor_legacy);
  157. labelStart = nextSlash;
  158. while (*labelStart == '/')
  159. labelStart++;
  160. nextSlash = String_strchrnul(labelStart, '/');
  161. if (nextSlash - labelStart > 0) {
  162. if (!StrBuf_putsz(s, w, isMonitor ? "[LXC:" : "[lxc:"))
  163. return false;
  164. if (!StrBuf_putsn(s, w, labelStart, nextSlash - labelStart))
  165. return false;
  166. if (!w(s, ']'))
  167. return false;
  168. cgroup = nextSlash;
  169. continue;
  170. }
  171. labelStart = cgroup;
  172. nextSlash = labelStart + labelLen;
  173. }
  174. if (Label_checkSuffix(labelStart, labelLen, str_service_suffix)) {
  175. const size_t serviceNameLen = labelLen - strlen(str_service_suffix);
  176. if (String_startsWith(cgroup, "user@")) {
  177. cgroup = nextSlash;
  178. while (*cgroup == '/')
  179. cgroup++;
  180. continue;
  181. }
  182. if (!StrBuf_putsn(s, w, cgroup, serviceNameLen))
  183. return false;
  184. cgroup = nextSlash;
  185. continue;
  186. }
  187. if (Label_checkSuffix(labelStart, labelLen, str_scope_suffix)) {
  188. const size_t scopeNameLen = labelLen - strlen(str_scope_suffix);
  189. if (Label_checkPrefix(labelStart, scopeNameLen, str_nspawn_scope_prefix)) {
  190. const size_t machineScopeNameLen = scopeNameLen - strlen(str_nspawn_scope_prefix);
  191. const bool is_monitor = String_startsWith(nextSlash, str_nspawn_monitor_label);
  192. if (!StrBuf_putsz(s, w, is_monitor ? "[SNC:" : "[snc:"))
  193. return false;
  194. if (!StrBuf_putsn(s, w, cgroup + strlen(str_nspawn_scope_prefix), machineScopeNameLen))
  195. return false;
  196. if (!w(s, ']'))
  197. return false;
  198. cgroup = nextSlash;
  199. if (String_startsWith(nextSlash, str_nspawn_monitor_label))
  200. cgroup += strlen(str_nspawn_monitor_label);
  201. else if (String_startsWith(nextSlash, str_nspawn_payload_label))
  202. cgroup += strlen(str_nspawn_payload_label);
  203. continue;
  204. } else if (Label_checkPrefix(labelStart, scopeNameLen, str_snap_scope_prefix)) {
  205. const char* nextDot = String_strchrnul(labelStart + strlen(str_snap_scope_prefix), '.');
  206. if (!StrBuf_putsz(s, w, "!snap:"))
  207. return false;
  208. if (nextDot >= labelStart + scopeNameLen) {
  209. nextDot = labelStart + scopeNameLen;
  210. }
  211. if (!StrBuf_putsn(s, w, labelStart + strlen(str_snap_scope_prefix), nextDot - (labelStart + strlen(str_snap_scope_prefix))))
  212. return false;
  213. cgroup = nextSlash;
  214. continue;
  215. } else if (Label_checkPrefix(labelStart, scopeNameLen, str_pod_scope_prefix)) {
  216. const char* nextDot = String_strchrnul(labelStart + strlen(str_pod_scope_prefix), '.');
  217. if (!StrBuf_putsz(s, w, "!pod:"))
  218. return false;
  219. if (nextDot >= labelStart + scopeNameLen) {
  220. nextDot = labelStart + scopeNameLen;
  221. }
  222. if (!StrBuf_putsn(s, w, labelStart + strlen(str_pod_scope_prefix),
  223. MINIMUM( nextDot - (labelStart + strlen(str_pod_scope_prefix)), 12)))
  224. return false;
  225. cgroup = nextSlash;
  226. continue;
  227. } else if (Label_checkPrefix(labelStart, scopeNameLen, str_docker_scope_prefix)) {
  228. const char* nextDot = String_strchrnul(labelStart + strlen(str_docker_scope_prefix), '.');
  229. if (!StrBuf_putsz(s, w, "!docker:"))
  230. return false;
  231. if (nextDot >= labelStart + scopeNameLen) {
  232. nextDot = labelStart + scopeNameLen;
  233. }
  234. if (!StrBuf_putsn(s, w, labelStart + strlen(str_docker_scope_prefix),
  235. MINIMUM( nextDot - (labelStart + strlen(str_docker_scope_prefix)), 12)))
  236. return false;
  237. cgroup = nextSlash;
  238. continue;
  239. }
  240. if (!w(s, '!'))
  241. return false;
  242. if (!StrBuf_putsn(s, w, cgroup, scopeNameLen))
  243. return false;
  244. cgroup = nextSlash;
  245. continue;
  246. }
  247. // Default behavior: Copy the full label
  248. cgroup = labelStart;
  249. if (!StrBuf_putsn(s, w, cgroup, labelLen))
  250. return false;
  251. cgroup = nextSlash;
  252. }
  253. return true;
  254. }
  255. char* CGroup_filterName(const char* cgroup) {
  256. StrBuf_state s = {
  257. .buf = NULL,
  258. .size = 0,
  259. .pos = 0,
  260. };
  261. if (!CGroup_filterName_internal(cgroup, &s, StrBuf_putc_count)) {
  262. return NULL;
  263. }
  264. s.buf = xCalloc(s.pos + 1, sizeof(char));
  265. s.size = s.pos;
  266. s.pos = 0;
  267. if (!CGroup_filterName_internal(cgroup, &s, StrBuf_putc_write)) {
  268. free(s.buf);
  269. return NULL;
  270. }
  271. s.buf[s.size] = '\0';
  272. return s.buf;
  273. }
  274. static bool CGroup_filterContainer_internal(const char* cgroup, StrBuf_state* s, StrBuf_putc_t w) {
  275. while (*cgroup) {
  276. if ('/' == *cgroup) {
  277. while ('/' == *cgroup)
  278. cgroup++;
  279. continue;
  280. }
  281. const char* labelStart = cgroup;
  282. const char* nextSlash = String_strchrnul(labelStart, '/');
  283. const size_t labelLen = nextSlash - labelStart;
  284. if (Label_checkPrefix(labelStart, labelLen, str_lxc_payload_prefix)) {
  285. const size_t cgroupNameLen = labelLen - strlen(str_lxc_payload_prefix);
  286. if (!StrBuf_putsz(s, w, "/lxc:"))
  287. return false;
  288. if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_payload_prefix), cgroupNameLen))
  289. return false;
  290. cgroup = nextSlash;
  291. continue;
  292. }
  293. // LXC legacy cgroup naming
  294. if (Label_checkEqual(labelStart, labelLen, str_lxc_payload_legacy)) {
  295. labelStart = nextSlash;
  296. while (*labelStart == '/')
  297. labelStart++;
  298. nextSlash = String_strchrnul(labelStart, '/');
  299. if (nextSlash - labelStart > 0) {
  300. if (!StrBuf_putsz(s, w, "/lxc:"))
  301. return false;
  302. if (!StrBuf_putsn(s, w, labelStart, nextSlash - labelStart))
  303. return false;
  304. cgroup = nextSlash;
  305. continue;
  306. }
  307. labelStart = cgroup;
  308. nextSlash = labelStart + labelLen;
  309. }
  310. if (Label_checkSuffix(labelStart, labelLen, str_scope_suffix)) {
  311. const size_t scopeNameLen = labelLen - strlen(str_scope_suffix);
  312. if (Label_checkPrefix(labelStart, scopeNameLen, str_nspawn_scope_prefix)) {
  313. const size_t machineScopeNameLen = scopeNameLen - strlen(str_nspawn_scope_prefix);
  314. const bool is_monitor = String_startsWith(nextSlash, str_nspawn_monitor_label);
  315. if (!is_monitor) {
  316. if (!StrBuf_putsz(s, w, "/snc:"))
  317. return false;
  318. if (!StrBuf_putsn(s, w, cgroup + strlen(str_nspawn_scope_prefix), machineScopeNameLen))
  319. return false;
  320. }
  321. cgroup = nextSlash;
  322. if (String_startsWith(nextSlash, str_nspawn_monitor_label))
  323. cgroup += strlen(str_nspawn_monitor_label);
  324. else if (String_startsWith(nextSlash, str_nspawn_payload_label))
  325. cgroup += strlen(str_nspawn_payload_label);
  326. continue;
  327. } else if (Label_checkPrefix(labelStart, scopeNameLen, str_pod_scope_prefix)) {
  328. const char* nextDot = String_strchrnul(labelStart + strlen(str_pod_scope_prefix), '.');
  329. if (!StrBuf_putsz(s, w, "/pod:"))
  330. return false;
  331. if (nextDot >= labelStart + scopeNameLen) {
  332. nextDot = labelStart + scopeNameLen;
  333. }
  334. if (!StrBuf_putsn(s, w, labelStart + strlen(str_pod_scope_prefix),
  335. MINIMUM( nextDot - (labelStart + strlen(str_pod_scope_prefix)), 12)))
  336. return false;
  337. cgroup = nextSlash;
  338. continue;
  339. } else if (Label_checkPrefix(labelStart, scopeNameLen, str_docker_scope_prefix)) {
  340. const char* nextDot = String_strchrnul(labelStart + strlen(str_docker_scope_prefix), '.');
  341. if (!StrBuf_putsz(s, w, "!docker:"))
  342. return false;
  343. if (nextDot >= labelStart + scopeNameLen) {
  344. nextDot = labelStart + scopeNameLen;
  345. }
  346. if (!StrBuf_putsn(s, w, labelStart + strlen(str_docker_scope_prefix),
  347. MINIMUM( nextDot - (labelStart + strlen(str_docker_scope_prefix)), 12)))
  348. return false;
  349. cgroup = nextSlash;
  350. continue;
  351. }
  352. cgroup = nextSlash;
  353. continue;
  354. }
  355. cgroup = nextSlash;
  356. }
  357. return true;
  358. }
  359. char* CGroup_filterContainer(const char* cgroup) {
  360. StrBuf_state s = {
  361. .buf = NULL,
  362. .size = 0,
  363. .pos = 0,
  364. };
  365. if (!CGroup_filterContainer_internal(cgroup, &s, StrBuf_putc_count)) {
  366. return NULL;
  367. }
  368. if (!s.pos) {
  369. return xStrdup("/");
  370. }
  371. s.buf = xCalloc(s.pos + 1, sizeof(char));
  372. s.size = s.pos;
  373. s.pos = 0;
  374. if (!CGroup_filterContainer_internal(cgroup, &s, StrBuf_putc_write)) {
  375. free(s.buf);
  376. return NULL;
  377. }
  378. s.buf[s.size] = '\0';
  379. return s.buf;
  380. }