intel_rdt: refactor pids monitoring code.
[collectd.git] / src / utils_proc_pids.c
1 #include "collectd.h"
2 #include "utils/common/common.h"
3 #include "utils_proc_pids.h"
4
5 #define UTIL_NAME "utils_proc_pids"
6
7 void pids_list_free(pids_list_t *list) {
8   assert(list);
9
10   pids_list_t *current = list;
11   while (current != NULL) {
12     pids_list_t *previous = current;
13     current = current->next;
14     sfree(previous);
15   }
16 }
17
18 int is_proc_name_valid(const char *name) {
19
20   if (name != NULL) {
21     unsigned len = strlen(name);
22     if (len > 0 && len <= MAX_PROC_NAME_LEN)
23       return 1;
24     else {
25       DEBUG(UTIL_NAME
26             ": Process name \'%s\' is too long. Max supported len is %d chars.",
27             name, MAX_PROC_NAME_LEN);
28     }
29   }
30
31   return 0;
32 }
33
34 int pids_list_add_pid(pids_list_t **list, const pid_t pid) {
35   assert(list);
36
37   pids_list_t *new_element = calloc(1, sizeof(*new_element));
38
39   if (new_element == NULL) {
40     ERROR(UTIL_NAME ": Alloc error\n");
41     return -1;
42   }
43   new_element->pid = pid;
44   new_element->next = NULL;
45
46   pids_list_t **current = list;
47   while (*current != NULL) {
48     current = &((*current)->next);
49   }
50   *current = new_element;
51   return 0;
52 }
53
54 int pids_list_contains_pid(pids_list_t *list, const pid_t pid) {
55   assert(list);
56
57   pids_list_t *current = list;
58   while (current != NULL) {
59     if (current->pid == pid)
60       return 1;
61     current = current->next;
62   }
63   return 0;
64 }
65
66 int pids_list_add_pids_list(pids_list_t **dst, pids_list_t *src,
67                             size_t *dst_num) {
68   assert(dst);
69   assert(src);
70   assert(dst_num);
71
72   pids_list_t *current = src;
73   int ret;
74
75   while (current != NULL) {
76     ret = pids_list_add_pid(dst, current->pid);
77     if (0 != ret)
78       return ret;
79
80     ++(*dst_num);
81     current = current->next;
82   }
83
84   return 0;
85 }
86
87 int read_proc_name(const char *procfs_path, const struct dirent *pid_entry,
88                    char *name, const size_t out_size) {
89   assert(procfs_path);
90   assert(pid_entry);
91   assert(name);
92   assert(out_size);
93   memset(name, 0, out_size);
94
95   const char *comm_file_name = "comm";
96
97   char *path = ssnprintf_alloc("%s/%s/%s", procfs_path, pid_entry->d_name,
98                                comm_file_name);
99   if (path == NULL)
100     return -1;
101   FILE *f = fopen(path, "r");
102   if (f == NULL) {
103     ERROR(UTIL_NAME ": Failed to open comm file, error: %d\n", errno);
104     sfree(path);
105     return -1;
106   }
107   size_t read_length = fread(name, sizeof(char), out_size, f);
108   name[out_size - 1] = '\0';
109   fclose(f);
110   sfree(path);
111   /* strip new line ending */
112   char *newline = strchr(name, '\n');
113   if (newline) {
114     *newline = '\0';
115   }
116
117   return read_length;
118 }
119
120 int get_pid_number(struct dirent *entry, pid_t *pid) {
121   char *tmp_end; /* used for strtoul error check*/
122
123   if (pid == NULL || entry == NULL)
124     return -1;
125
126   if (entry->d_type != DT_DIR)
127     return -1;
128
129   /* trying to get pid number from directory name*/
130   *pid = strtoul(entry->d_name, &tmp_end, 10);
131   if (*tmp_end != '\0') {
132     return -1; /* conversion failed, not proc-pid */
133   }
134   /* all checks passed, marking as success */
135   return 0;
136 }
137
138 void pids_list_to_array(pid_t *array, pids_list_t *list,
139                         const size_t array_length) {
140
141   assert(list);
142   assert(array);
143   assert(array_length > 0);
144
145   size_t current = 0;
146
147   while (list != NULL && current < array_length) {
148     array[current] = list->pid;
149     list = list->next;
150     ++current;
151   }
152 }
153
154 int initialize_proc_pids(const char **procs_names_array,
155                          const size_t procs_names_array_size,
156                          proc_pids_t **proc_pids_array) {
157
158   assert(proc_pids_array);
159   assert(NULL == *proc_pids_array);
160
161   /* Copy procs names to output array. Initialize pids list with NULL value. */
162   (*proc_pids_array) =
163       calloc(procs_names_array_size, sizeof(**proc_pids_array));
164
165   if (NULL == (*proc_pids_array))
166     return -1;
167
168   for (size_t i = 0; i < procs_names_array_size; ++i) {
169     sstrncpy((*proc_pids_array)[i].proccess_name, procs_names_array[i],
170              STATIC_ARRAY_SIZE((*proc_pids_array)[i].proccess_name));
171     (*proc_pids_array)[i].pids = NULL;
172   }
173
174   return 0;
175 }
176
177 int fetch_pids_for_procs(const char *procfs_path,
178                          const char **procs_names_array,
179                          const size_t procs_names_array_size,
180                          proc_pids_t **proc_pids_array) {
181   assert(procfs_path);
182   assert(procs_names_array);
183   assert(procs_names_array_size);
184
185   DIR *proc_dir = opendir(procfs_path);
186   if (proc_dir == NULL) {
187     ERROR(UTIL_NAME ": Could not open %s directory, error: %d", procfs_path,
188           errno);
189     return -1;
190   }
191
192   int init_result = initialize_proc_pids(
193       procs_names_array, procs_names_array_size, proc_pids_array);
194   if (0 != init_result)
195     return -1;
196
197   /* Go through procfs and find PIDS and their comms */
198   struct dirent *entry;
199   while ((entry = readdir(proc_dir)) != NULL) {
200
201     pid_t pid;
202     int pid_conversion = get_pid_number(entry, &pid);
203     if (pid_conversion < 0)
204       continue;
205
206     proc_comm_t comm;
207     int read_result =
208         read_proc_name(procfs_path, entry, comm, sizeof(proc_comm_t));
209     if (read_result <= 0) {
210       ERROR(UTIL_NAME ": Comm file skipped. Read result: %d", read_result);
211       continue;
212     }
213
214     /* Try to find comm in input procs array (proc_pids_array has same names) */
215     for (size_t i = 0; i < procs_names_array_size; ++i) {
216       if (0 == strncmp(comm, (*proc_pids_array)[i].proccess_name,
217                        STATIC_ARRAY_SIZE(comm)))
218         pids_list_add_pid(&((*proc_pids_array)[i].pids), pid);
219     }
220   }
221
222   int close_result = closedir(proc_dir);
223   if (0 != close_result) {
224     ERROR(UTIL_NAME ": failed to close %s directory, error: %d", procfs_path,
225           errno);
226     sfree((*proc_pids_array));
227     return -1;
228   }
229   return 0;
230 }
231
232 int pids_list_diff(pids_list_t *prev, pids_list_t *curr, pids_list_t **added,
233                    size_t *added_num, pids_list_t **removed,
234                    size_t *removed_num) {
235   assert(prev || curr);
236   assert(added);
237   assert(removed);
238
239   if (NULL == prev) {
240     /* append all PIDs from curr to added*/
241     return pids_list_add_pids_list(added, curr, added_num);
242   } else if (NULL == curr) {
243     /* append all PIDs from prev to removed*/
244     return pids_list_add_pids_list(removed, prev, removed_num);
245   }
246
247   pids_list_t *item = prev;
248   while (item != NULL) {
249     if (0 == pids_list_contains_pid(curr, item->pid)) {
250       int add_result = pids_list_add_pid(removed, item->pid);
251       if (add_result < 0)
252         return add_result;
253       ++(*removed_num);
254     }
255     item = item->next;
256   }
257
258   item = curr;
259   while (item != NULL) {
260     if (0 == pids_list_contains_pid(prev, item->pid)) {
261       int add_result = pids_list_add_pid(added, item->pid);
262       if (add_result < 0)
263         return add_result;
264       ++(*added_num);
265     }
266     item = item->next;
267   }
268
269   return 0;
270 }