intel_rdt: close file descriptor on error
[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     closedir(proc_dir);
196     return -1;
197   }
198
199   /* Go through procfs and find PIDS and their comms */
200   struct dirent *entry;
201   while ((entry = readdir(proc_dir)) != NULL) {
202
203     pid_t pid;
204     int pid_conversion = get_pid_number(entry, &pid);
205     if (pid_conversion < 0)
206       continue;
207
208     proc_comm_t comm;
209     int read_result =
210         read_proc_name(procfs_path, entry, comm, sizeof(proc_comm_t));
211     if (read_result <= 0) {
212       ERROR(UTIL_NAME ": Comm file skipped. Read result: %d", read_result);
213       continue;
214     }
215
216     /* Try to find comm in input procs array (proc_pids_array has same names) */
217     for (size_t i = 0; i < procs_names_array_size; ++i) {
218       if (0 == strncmp(comm, (*proc_pids_array)[i].proccess_name,
219                        STATIC_ARRAY_SIZE(comm)))
220         pids_list_add_pid(&((*proc_pids_array)[i].pids), pid);
221     }
222   }
223
224   int close_result = closedir(proc_dir);
225   if (0 != close_result) {
226     ERROR(UTIL_NAME ": failed to close %s directory, error: %d", procfs_path,
227           errno);
228     sfree((*proc_pids_array));
229     return -1;
230   }
231   return 0;
232 }
233
234 int pids_list_diff(pids_list_t *prev, pids_list_t *curr, pids_list_t **added,
235                    size_t *added_num, pids_list_t **removed,
236                    size_t *removed_num) {
237   assert(prev || curr);
238   assert(added);
239   assert(removed);
240
241   if (NULL == prev) {
242     /* append all PIDs from curr to added*/
243     return pids_list_add_pids_list(added, curr, added_num);
244   } else if (NULL == curr) {
245     /* append all PIDs from prev to removed*/
246     return pids_list_add_pids_list(removed, prev, removed_num);
247   }
248
249   pids_list_t *item = prev;
250   while (item != NULL) {
251     if (0 == pids_list_contains_pid(curr, item->pid)) {
252       int add_result = pids_list_add_pid(removed, item->pid);
253       if (add_result < 0)
254         return add_result;
255       ++(*removed_num);
256     }
257     item = item->next;
258   }
259
260   item = curr;
261   while (item != NULL) {
262     if (0 == pids_list_contains_pid(prev, item->pid)) {
263       int add_result = pids_list_add_pid(added, item->pid);
264       if (add_result < 0)
265         return add_result;
266       ++(*added_num);
267     }
268     item = item->next;
269   }
270
271   return 0;
272 }