4c6f8b8c83c91a2171ff6cca79a9968b6a3b97ce
[platform/core/appfw/aul-1.git] / am_daemon / amd_cgutil.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <string.h>
5 #include <sys/mount.h>
6 #include <sys/stat.h>
7 #include <sys/types.h>
8 #include <unistd.h>
9 #include <errno.h>
10 #include <limits.h>
11 #include <dirent.h>
12 #include <mntent.h>
13
14
15 #include "amd_config.h"
16 #include "simple_util.h"
17 #include "amd_cgutil.h"
18
19 #define CFILE_RELEASE_AGENT "release_agent"
20 #define CFILE_NOTIFY_ON_RELEASE "notify_on_release"
21 #define CFILE_TASKS "tasks"
22
23 #define DEFAULT_MODE 0755
24
25 struct ctrl {
26         enum ctrl_type ctrl;
27         char *root; /* cginfo's mount_point + subsystem name */
28 };
29
30 struct cginfo {
31         int refcnt;
32         char *mount_point;
33         struct ctrl ctrls[CTRL_MAX];
34 };
35
36 enum {
37         _MNT_ROOT,
38         _MNT_MGR,
39         _MNT_CPU,
40         _MNT_MEM,
41         /* add type after adding mount information to mntinfos[] */
42 };
43
44 struct mntinfo {
45         char *source;
46         char *ftype; /* filesystem type */
47         unsigned long flags;
48         char *option;
49 };
50
51 static struct mntinfo mntinfos[] = {
52         [_MNT_ROOT] = {
53                 "cgroup_root",
54                 "tmpfs",
55                 MS_NODEV | MS_NOSUID | MS_NOEXEC,
56                 NULL,
57         },
58         [_MNT_MGR] = {
59                 "daemon_mgr",
60                 "cgroup",
61                 MS_NODEV | MS_NOSUID | MS_NOEXEC,
62                 "none,name=daemon_mgr",
63         },
64         [_MNT_CPU] = {
65                 "cpuset",
66                 "cgroup",
67                 MS_NODEV | MS_NOSUID | MS_NOEXEC,
68                 "cpuset",
69         },
70         [_MNT_MEM] = {
71                 "memory",
72                 "cgroup",
73                 MS_NODEV | MS_NOSUID | MS_NOEXEC,
74                 "memory",
75         },
76         /* TODO: add more */
77 };
78
79 struct ctrlinfo {
80         int mnt_type;
81         char *name;
82 };
83
84 static struct ctrlinfo ctrlinfos[] = {
85         [CTRL_MGR] = { _MNT_MGR, "mgr", },
86 #if defined(USE_CGROUP_CPU)
87         [CTRL_CPU] = { _MNT_CPU, "cpuset", },
88 #endif
89 #if defined(USE_CGROUP_MEM)
90         [CTRL_MEM] = { _MNT_MEM, "memory", },
91 #endif
92 };
93
94 static inline int _mount(const char *mount_point, struct mntinfo *mti)
95 {
96         assert(mti);
97         return mount(mti->source, mount_point, mti->ftype, mti->flags,
98                         mti->option);
99 }
100
101 static inline int _umount(const char *mount_point)
102 {
103         return umount2(mount_point, MNT_DETACH);
104 }
105
106 static int _write_single(struct ctrl *ctr,
107                 const char *group, const char *file, const char *str)
108 {
109         FILE *fp;
110         char path[FILENAME_MAX];
111         int r;
112
113         assert(ctr);
114         assert(file);
115
116         if (!str)
117                 str = "";
118
119         snprintf(path, sizeof(path), "%s/%s%s%s", ctr->root,
120                         group ? : "", group ? "/" : "", file);
121         fp = fopen(path, "w");
122         if (!fp) {
123                 _E("open: %s: %s", path, strerror(errno));
124                 return -1;
125         }
126
127         r = fputs(str, fp);
128         if (r == EOF) {
129                 _E("write: %s,%s: %s", path, str, strerror(errno));
130                 r = -1;
131         } else {
132                 r = 0;
133         }
134
135         fclose(fp);
136
137         return r;
138 }
139
140 /*
141 static void _trunc_newline(char *buf, int sz)
142 {
143         char *s;
144
145         assert(buf);
146         assert(sz > 0);
147
148         s = buf;
149         while (*s) {
150                 if (*s == '\r' || *s == '\n') {
151                         *s = '\0';
152                         break;
153                 }
154                 s++;
155         }
156 }
157
158 static int _read_single(struct ctrl *ctr, const char *file, char *buf, int sz)
159 {
160         FILE *fp;
161         char path[FILENAME_MAX];
162         char _buf[LINE_MAX];
163         char *r;
164
165         assert(ctr);
166         assert(file);
167         assert(buf);
168         assert(sz > 0);
169
170         snprintf(path, sizeof(path), "%s/%s", ctr->root, file);
171         fp = fopen(path, "r");
172         if (!fp) {
173                 _E("open: %s: %s", path, strerror(errno));
174                 return -1;
175         }
176
177         r = fgets(_buf, sizeof(_buf), fp);
178         if (r == NULL) {
179                 _E("read: %s: %s", path, strerror(errno));
180         } else {
181                 _trunc_newline(_buf, sizeof(_buf));
182                 snprintf(buf, sz, "%s", _buf);
183         }
184
185         fclose(fp);
186
187         return r == NULL ? -1 : 0;
188 }
189 */
190
191 static int _destroy(struct cginfo *cg)
192 {
193         int i;
194         int r;
195
196         for (i = 0; i < sizeof(cg->ctrls)/sizeof(cg->ctrls[0]); i++) {
197                 struct ctrl *ctr = &cg->ctrls[i];
198
199                 if (ctr->root) {
200                         r = _umount(ctr->root);
201                         if (r == -1) {
202                                 _E("unmount: %s: %s",
203                                                 ctr->root, strerror(errno));
204                                 return -1;
205                         }
206                         free(ctr->root);
207                         ctr->root = NULL;
208                 }
209         }
210
211         if (cg->mount_point) {
212                 r = _umount(cg->mount_point);
213                 if (r == -1) {
214                         _E("unmount: %s: %s",
215                                         cg->mount_point, strerror(errno));
216                         return -1;
217                 }
218                 free(cg->mount_point);
219         }
220
221         free(cg);
222
223         return 0;
224 }
225
226 void cgutil_destroy(struct cginfo **cg)
227 {
228         int r;
229
230         if (!cg || !*cg)
231                 return;
232
233         (*cg)->refcnt--;
234
235         if ((*cg)->refcnt > 0)
236                 return;
237
238         r = _destroy(*cg);
239         if (r == -1)
240                 return;
241
242         *cg = NULL;
243 }
244
245 static int check_cgroup_mount(const char *mount_point)
246 {
247         struct mntent* mnt;
248         const char* table = "/etc/mtab";
249         FILE* fp;
250         int     r = -1;
251         fp = setmntent(table, "r");
252
253         if (!fp)
254                 return r;
255
256         while (mnt=getmntent(fp)) {
257                 if (strcmp(mount_point, mnt->mnt_dir) == 0) {
258                         r = 0;
259                         break;
260                 }
261         }
262         endmntent(fp);
263         return r;
264 }
265
266 static int _mount_root(struct cginfo *cg, const char *mount_point)
267 {
268         int r = 0;
269
270         if (check_cgroup_mount(mount_point) < 0)
271                 r = _mount(mount_point, &mntinfos[_MNT_ROOT]);
272         if (r == -1) {
273                 if (errno != EBUSY) {
274                         _E("mount: %s: %s", mount_point, strerror(errno));
275                         return -1;
276                 }
277                 _D("'%s' already mounted", mount_point);
278         }
279
280         cg->mount_point = strdup(mount_point);
281
282         return 0;
283 }
284
285 static int _init_ctrl(struct ctrl *ctr, struct cginfo *cg, enum ctrl_type ctrl)
286 {
287         int r;
288         int mt;
289         char path[FILENAME_MAX];
290
291         assert(ctr);
292         assert(ctrl >= 0);
293         assert(ctrl < sizeof(ctrlinfos)/sizeof(ctrlinfos[0]));
294
295         mt = ctrlinfos[ctrl].mnt_type;
296         snprintf(path, sizeof(path), "%s/%s",
297                         cg->mount_point, ctrlinfos[ctrl].name);
298
299         /* TODO: read /proc/cgroup and check the current type is enabled */
300
301         r = mkdir(path, DEFAULT_MODE);
302         if (r == -1) {
303                 if (errno != EEXIST) {
304                         _E("mkdir: %s: %s", path, strerror(errno));
305                         return -1;
306                 }
307                 _D("'%s' exist", path);
308         }
309
310         r = _mount(path, &mntinfos[mt]);
311         if (r == -1) {
312                 if (errno != EBUSY) {
313                         _E("mount: %s: %s", path, strerror(errno));
314                         rmdir(path);
315                         return -1;
316                 }
317                 _D("'%s' already mounted", path);
318         }
319
320         ctr->ctrl = ctrl;
321         ctr->root = strdup(path);
322
323         return 0;
324 }
325
326 static int _set_release_agent(struct ctrl *ctr, const char *agent_path)
327 {
328         int r;
329
330         r = access(agent_path, X_OK);
331         if (r == -1) {
332                 _E("'%s' is not executable: %s",
333                                 agent_path, strerror(errno));
334                 return -1;
335         }
336
337         r = _write_single(ctr, NULL, CFILE_RELEASE_AGENT, agent_path);
338         if (r == -1)
339                 return -1;
340
341         r = _write_single(ctr, NULL, CFILE_NOTIFY_ON_RELEASE, "1");
342         if (r == -1)
343                 return -1;
344
345         return 0;
346 }
347
348 struct cginfo *cgutil_ref(struct cginfo *cg)
349 {
350         if (cg)
351                 cg->refcnt++;
352
353         return cg;
354 }
355
356 static int _create(struct cginfo *cg,
357                 const char *mount_point, const char *agent_path)
358 {
359         int i;
360         int r;
361
362         r = _mount_root(cg, mount_point);
363         if (r == -1)
364                 return -1;
365
366         for (i = 0; i < sizeof(cg->ctrls)/sizeof(cg->ctrls[0]); i++) {
367                 r = _init_ctrl(&cg->ctrls[i], cg, i);
368                 if (r == -1)
369                         return -1;
370         }
371
372         r = _set_release_agent(&cg->ctrls[CTRL_MGR], agent_path);
373         if (r == -1)
374                 return -1;
375
376         return 0;
377 }
378
379 int cgutil_create(const char *mount_point, const char *agent_path,
380                 struct cginfo **cg)
381 {
382         struct cginfo *_cg;
383         int r;
384
385         if (!mount_point || !*mount_point ||
386                         !agent_path || !*agent_path || !cg) {
387                 errno = EINVAL;
388                 _E("cgutil create: %s", strerror(errno));
389                 return -1;
390         }
391
392         _cg = calloc(1, sizeof(*_cg));
393         if (!_cg) {
394                 _E("cgutil alloc: %s", strerror(errno));
395                 return -1;
396         }
397
398         r = _create(_cg, mount_point, agent_path);
399         if (r == -1) {
400                 _destroy(_cg);
401                 return -1;
402         }
403
404         *cg = cgutil_ref(_cg);
405
406         return 0;
407 }
408
409 int cgutil_create_group(struct cginfo *cg,
410                 enum ctrl_type ctrl, const char *group)
411 {
412         int r;
413         struct ctrl *ctr;
414         char path[FILENAME_MAX];
415
416         assert(ctrl >= 0 && ctrl < CTRL_MAX);
417
418         if (!cg || !group || !*group) {
419                 errno = EINVAL;
420                 _E("cgutil create group: %s", strerror(errno));
421                 return -1;
422         }
423
424         ctr = &cg->ctrls[ctrl];
425         assert(ctr);
426
427         snprintf(path, sizeof(path), "%s/%s", ctr->root, FILENAME(group));
428
429         r = mkdir(path, DEFAULT_MODE);
430         if (r == -1) {
431                 _E("cgutil create group: mkdir: %s", strerror(errno));
432                 return -1;
433         }
434
435         return 0;
436 }
437
438 int cgutil_remove_group(struct cginfo *cg,
439                 enum ctrl_type ctrl, const char *group)
440 {
441         int r;
442         struct ctrl *ctr;
443         char path[FILENAME_MAX];
444
445         assert(ctrl >= 0 && ctrl < CTRL_MAX);
446
447         if (!cg || !group || !*group) {
448                 errno = EINVAL;
449                 _E("cgutil remove group: %s", strerror(errno));
450                 return -1;
451         }
452
453         ctr = &cg->ctrls[ctrl];
454         assert(ctr);
455
456         snprintf(path, sizeof(path), "%s/%s", ctr->root, FILENAME(group));
457
458         r = rmdir(path);
459         if (r == -1) {
460                 _E("cgutil remove group: rmdir: %s", strerror(errno));
461                 return -1;
462         }
463
464         return 0;
465 }
466
467 int cgutil_exist_group(struct cginfo *cg,
468                 enum ctrl_type ctrl, const char *group)
469 {
470         int r;
471         struct ctrl *ctr;
472         char path[FILENAME_MAX];
473         struct stat st;
474
475         assert(ctrl >= 0 && ctrl < CTRL_MAX);
476
477         if (!cg || !group || !*group) {
478                 errno = EINVAL;
479                 _E("cgutil exist group: %s", strerror(errno));
480                 return -1;
481         }
482
483         ctr = &cg->ctrls[ctrl];
484         assert(ctr);
485
486         snprintf(path, sizeof(path), "%s/%s", ctr->root, FILENAME(group));
487
488         r = stat(path, &st);
489         if (r == -1) {
490                 if (errno == ENOENT)
491                         return 0;
492
493                 _E("cgutil exist group: %s", strerror(errno));
494                 return -1;
495         }
496
497         /* stat() returns no error, then group exists */
498         return 1;
499 }
500
501 int cgutil_group_add_pid(struct cginfo *cg,
502                 enum ctrl_type ctrl, const char *group, pid_t pid)
503 {
504         int r;
505         struct ctrl *ctr;
506         char buf[32]; /* 32 is enough for ineger number */
507
508         assert(ctrl >= 0 && ctrl < CTRL_MAX);
509
510         /* PID 1 is init's PID */
511         if (!cg || !group || !*group || pid <= 1) {
512                 errno = EINVAL;
513                 _E("cgutil add pid: %s", strerror(errno));
514                 return -1;
515         }
516
517         ctr = &cg->ctrls[ctrl];
518         assert(ctr);
519
520         snprintf(buf, sizeof(buf), "%d", pid);
521
522         r = _write_single(ctr, FILENAME(group), CFILE_TASKS, buf);
523         if (r == -1)
524                 return -1;
525
526         return 0;
527 }
528
529 static int _foreach_pid(struct ctrl *ctr, const char *group,
530                 cgutil_iter_pid_callback cb, void *user_data)
531 {
532         int r;
533         FILE *fp;
534         char path[FILENAME_MAX];
535
536         assert(ctr);
537         assert(cb);
538
539         snprintf(path, sizeof(path), "%s/%s/%s",
540                         ctr->root, FILENAME(group), CFILE_TASKS);
541         fp = fopen(path, "r");
542         if (!fp) {
543                 _E("open: %s: %s", path, strerror(errno));
544                 return -1;
545         }
546
547         while (!feof(fp)) {
548                 unsigned long l;
549
550                 r = fscanf(fp, "%lu", &l);
551                 if (r != 1)
552                         break;
553
554                 r = cb(user_data, group, l);
555                 if (r == -1)
556                         break;
557         }
558
559         fclose(fp);
560
561         return 0;
562 }
563
564 int cgutil_group_foreach_pid(struct cginfo *cg,
565                 enum ctrl_type ctrl, const char *group,
566                 cgutil_iter_pid_callback cb, void *user_data)
567 {
568         struct ctrl *ctr;
569
570         assert(ctrl >= 0 && ctrl < CTRL_MAX);
571
572         if (!cg || !group || !*group || !cb) {
573                 errno = EINVAL;
574                 _E("cgutil foreach pid: %s", strerror(errno));
575                 return -1;
576         }
577
578         ctr = &cg->ctrls[ctrl];
579         assert(ctr);
580
581         return _foreach_pid(ctr, group, cb, user_data);
582 }
583
584 static int _foreach_group(struct ctrl *ctr,
585                 cgutil_iter_group_callback cb, void *user_data)
586 {
587         int r;
588         DIR *d;
589         struct dirent *de;
590
591         assert(ctr);
592         assert(cb);
593
594         d = opendir(ctr->root);
595         if (!d) {
596                 _E("open: %s: %s", ctr->root, strerror(errno));
597                 return -1;
598         }
599
600         for (de = readdir(d); de; de = readdir(d)) {
601                 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
602                         continue;
603
604                 r = cb(user_data, de->d_name);
605                 if (r == -1)
606                         break;
607         }
608
609         closedir(d);
610
611         return 0;
612 }
613
614 int cgutil_foreach_group(struct cginfo *cg, enum ctrl_type ctrl,
615                 cgutil_iter_group_callback cb, void *user_data)
616 {
617         struct ctrl *ctr;
618
619         assert(ctrl >= 0 && ctrl < CTRL_MAX);
620
621         if (!cg || !cb) {
622                 errno = EINVAL;
623                 _E("cgutil foreach group: %s", strerror(errno));
624                 return -1;
625         }
626
627         ctr = &cg->ctrls[ctrl];
628         assert(ctr);
629
630         return _foreach_group(ctr, cb, user_data);
631 }