Fix native compilation
[src/app-framework-main.git] / src / utils-systemd.c
1 /*
2  Copyright (C) 2015-2020 IoT.bzh
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
6  Licensed under the Apache License, Version 2.0 (the "License");
7  you may not use this file except in compliance with the License.
8  You may obtain a copy of the License at
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 */
18 #define _GNU_SOURCE
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <dirent.h>
25 #include <fcntl.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <time.h>
30
31 #ifndef NO_LIBSYSTEMD
32 # include <systemd/sd-bus.h>
33 # include <systemd/sd-bus-protocol.h>
34 #else
35   struct sd_bus;
36   struct sd_bus_message;
37   typedef struct { const char *name; const char *message; } sd_bus_error;
38 # define sd_bus_unref(...)                ((void)0)
39 # define sd_bus_default_user(p)           ((*(p)=NULL),(-ENOTSUP))
40 # define sd_bus_default_system(p)         ((*(p)=NULL),(-ENOTSUP))
41 # define sd_bus_call_method(...)          (-ENOTSUP)
42 # define SD_BUS_ERROR_NULL                {NULL,NULL}
43 # define sd_bus_message_read_basic(...)   (-ENOTSUP)
44 # define sd_bus_message_unref(...)        (NULL)
45 # define sd_bus_get_property_string(...)  (-ENOTSUP)
46 # define sd_bus_get_property_trivial(...) (-ENOTSUP)
47 #endif
48
49 #include "utils-systemd.h"
50
51 #if !defined(SYSTEMD_UNITS_ROOT)
52 # define SYSTEMD_UNITS_ROOT "/usr/local/lib/systemd"
53 #endif
54
55 static const char sdb_path[] = "/org/freedesktop/systemd1";
56 static const char sdb_destination[] = "org.freedesktop.systemd1";
57 static const char sdbi_manager[] = "org.freedesktop.systemd1.Manager";
58 static const char sdbi_unit[] = "org.freedesktop.systemd1.Unit";
59 static const char sdbi_service[] = "org.freedesktop.systemd1.Service";
60 static const char sdbi_job[] = "org.freedesktop.systemd1.Job";
61 static const char sdbj_state[] = "State";
62 static const char sdbm_reload[] = "Reload";
63 static const char sdbm_start_unit[] = "StartUnit";
64 static const char sdbm_restart_unit[] = "RestartUnit";
65 static const char sdbm_stop_unit[] = "StopUnit";
66 static const char sdbm_start[] = "Start";
67 static const char sdbm_restart[] = "Restart";
68 static const char sdbm_stop[] = "Stop";
69 static const char sdbm_get_unit[] = "GetUnit";
70 static const char sdbm_get_unit_by_pid[] = "GetUnitByPID";
71 static const char sdbm_load_unit[] = "LoadUnit";
72 static const char sdbp_active_state[] = "ActiveState";
73 static const char sdbp_exec_main_pid[] = "ExecMainPID";
74
75 static const char *sds_state_names[] = {
76         NULL,
77         "inactive",
78         "activating",
79         "active",
80         "deactivating",
81         "reloading",
82         "failed"
83 };
84
85 static const char *sds_job_state_names[] = {
86         NULL,
87         "waiting",
88         "running"
89 };
90
91 static struct sd_bus *sysbus;
92 static struct sd_bus *usrbus;
93
94 /*
95  * Translate systemd errors to errno errors
96  */
97 static int seterrno(int value)
98 {
99         errno = value;
100         return -1;
101 }
102
103 static int sderr2errno(int rc)
104 {
105         return rc < 0 ? seterrno(-rc) : rc;
106 }
107
108 static int errno2sderr(int rc)
109 {
110         return rc < 0 ? -errno : rc;
111 }
112
113 /*
114  * Returns in 'ret' either the system bus (if isuser==0)
115  * or the user bus (if isuser!=0).
116  * Returns 0 in case of success or -1 in case of error
117  */
118 int systemd_get_bus(int isuser, struct sd_bus **ret)
119 {
120         int rc;
121         struct sd_bus *bus;
122
123         bus = isuser ? usrbus : sysbus;
124         if (bus)
125                 *ret = bus;
126         else if (isuser) {
127                 rc = sd_bus_default_user(ret);
128                 if (rc < 0)
129                         goto error;
130                 usrbus = *ret;
131         } else {
132                 rc = sd_bus_default_system(ret);
133                 if (rc < 0)
134                         goto error;
135                 sysbus = *ret;
136         }
137         return 0;
138 error:
139         return sderr2errno(rc);
140 }
141
142 void systemd_set_bus(int isuser, struct sd_bus *bus)
143 {
144         struct sd_bus **target = isuser ? &usrbus : &sysbus;
145         if (*target)
146                 sd_bus_unref(*target);
147         *target = bus;
148 }
149
150 #if 0
151 /********************************************************************
152  * routines for escaping unit names to compute dbus path of units
153  *******************************************************************/
154 /*
155  * Should the char 'c' be escaped?
156  */
157 static inline int should_escape_for_path(char c)
158 {
159         if (c >= 'A') {
160                 return c <= (c >= 'a' ? 'z' : 'Z');
161         } else {
162                 return c >= '0' && c <= '9';
163         }
164 }
165
166 /*
167  * ascii char for the hexadecimal digit 'x'
168  */
169 static inline char d2h(int x)
170 {
171         return (char)(x + (x > 9 ? ('a' - 10) : '0'));
172 }
173
174 /*
175  * escapes in 'path' of 'pathlen' the 'unit'
176  * returns 0 in case of success or -1 in case of error
177  */
178 static int unit_escape_for_path(char *path, size_t pathlen, const char *unit)
179 {
180         size_t r, w;
181         char c;
182
183         c = unit[r = w = 0];
184         while (c) {
185                 if (should_escape_for_path(c)) {
186                         if (w + 2 >= pathlen)
187                                 goto toolong;
188                         path[w++] = '_';
189                         path[w++] = d2h((c >> 4) & 15);;
190                         path[w++] = d2h(c & 15);
191                 } else {
192                         if (w >= pathlen)
193                                 goto toolong;
194                         path[w++] = c;
195                 }
196                 c = unit[++r];
197         }
198         if (w >= pathlen)
199                 goto toolong;
200         path[w] = 0;
201         return 0;
202 toolong:
203         return seterrno(ENAMETOOLONG);
204 }
205 #endif
206
207 /********************************************************************
208  * Routines for getting paths
209  *******************************************************************/
210
211 static char *get_dpath(struct sd_bus_message *msg)
212 {
213         int rc;
214         const char *reply;
215         char *result;
216
217         rc = sd_bus_message_read_basic(msg, 'o', &reply);
218         rc = sderr2errno(rc);
219         if (rc < 0)
220                 result = NULL;
221         else {
222                 result = strdup(reply);
223                 if (!result)
224                         errno = ENOMEM;
225         }
226         sd_bus_message_unref(msg);
227         return result;
228 }
229
230 static char *get_unit_dpath(struct sd_bus *bus, const char *unit, int load)
231 {
232         int rc;
233         struct sd_bus_message *ret = NULL;
234         sd_bus_error err = SD_BUS_ERROR_NULL;
235
236         rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, load ? sdbm_load_unit : sdbm_get_unit, &err, &ret, "s", unit);
237         if (rc < 0)
238                 goto error;
239
240         return get_dpath(ret);
241 error:
242         sd_bus_message_unref(ret);
243         return NULL;
244 }
245
246 static char *get_unit_dpath_by_pid(struct sd_bus *bus, unsigned pid)
247 {
248         int rc;
249         struct sd_bus_message *ret = NULL;
250         sd_bus_error err = SD_BUS_ERROR_NULL;
251
252         rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, sdbm_get_unit_by_pid, &err, &ret, "u", pid);
253         if (rc < 0)
254                 goto error;
255
256         return get_dpath(ret);
257 error:
258         sd_bus_message_unref(ret);
259         return NULL;
260 }
261
262 static int unit_pid(struct sd_bus *bus, const char *dpath)
263 {
264         int rc;
265         unsigned u = 0;
266         sd_bus_error err = SD_BUS_ERROR_NULL;
267
268         rc = sd_bus_get_property_trivial(bus, sdb_destination, dpath, sdbi_service, sdbp_exec_main_pid, &err, 'u', &u);
269         return rc < 0 ? rc : (int)u;
270 }
271
272 static enum SysD_State unit_state(struct sd_bus *bus, const char *dpath)
273 {
274         int rc;
275         char *st;
276         enum SysD_State resu;
277         sd_bus_error err = SD_BUS_ERROR_NULL;
278
279         resu = SysD_State_INVALID;
280         rc = sd_bus_get_property_string(bus, sdb_destination, dpath, sdbi_unit, sdbp_active_state, &err, &st);
281         if (rc < 0) {
282                 errno = -rc;
283         } else {
284                 switch (st[0]) {
285                 case 'a':
286                         if (!strcmp(st, sds_state_names[SysD_State_Active]))
287                                 resu = SysD_State_Active;
288                         else if (!strcmp(st, sds_state_names[SysD_State_Activating]))
289                                 resu = SysD_State_Activating;
290                         break;
291                 case 'd':
292                         if (!strcmp(st, sds_state_names[SysD_State_Deactivating]))
293                                 resu = SysD_State_Deactivating;
294                         break;
295                 case 'f':
296                         if (!strcmp(st, sds_state_names[SysD_State_Failed]))
297                                 resu = SysD_State_Failed;
298                         break;
299                 case 'i':
300                         if (!strcmp(st, sds_state_names[SysD_State_Inactive]))
301                                 resu = SysD_State_Inactive;
302                         break;
303                 case 'r':
304                         if (!strcmp(st, sds_state_names[SysD_State_Reloading]))
305                                 resu = SysD_State_Reloading;
306                         break;
307                 default:
308                         break;
309                 }
310                 if (resu == NULL)
311                         errno = EBADMSG;
312                 free(st);
313         }
314         return resu;
315 }
316
317 static int job_wait(struct sd_bus *bus, struct sd_bus_message *job)
318 {
319         int rc;
320         sd_bus_error err = SD_BUS_ERROR_NULL;
321         const char *jpath = NULL;
322         char *jstate;
323         struct timespec tispec;
324         const int period_ms = 10;
325         const int trial_s = 10;
326         const int trial_count = (trial_s * 1000) / period_ms;
327         const int period_ns = period_ms * 1000000;
328         int trial;
329
330         /* Get job path */
331         rc = sd_bus_message_read_basic(job, 'o', &jpath);
332         if (rc < 0)
333                 return rc;
334
335         /* Wait for job to enter "running" state */
336         rc = 0;
337         for (trial = 1 ; trial <= trial_count ; trial++) {
338                 jstate = NULL;
339                 if(sd_bus_get_property_string(bus, sdb_destination, jpath, sdbi_job, sdbj_state, &err, &jstate) >= 0) {
340                         if(jstate && strcmp(jstate, sds_job_state_names[SysD_Job_State_Running]) == 0) {
341                                 free(jstate);
342                                 break;
343                         } else {
344                                 tispec.tv_sec = 0;
345                                 tispec.tv_nsec = period_ns;
346                                 nanosleep(&tispec, NULL);
347                         }
348                         free(jstate);
349                 }
350         }
351         if(trial > trial_count)
352                 rc = -1;
353
354         return rc;
355 }
356
357 static int unit_start(struct sd_bus *bus, const char *dpath)
358 {
359         int rc;
360         struct sd_bus_message *ret = NULL;
361         sd_bus_error err = SD_BUS_ERROR_NULL;
362
363         rc = sd_bus_call_method(bus, sdb_destination, dpath, sdbi_unit, sdbm_start, &err, &ret, "s", "replace");
364         if(ret) {
365                 rc = job_wait(bus, ret);
366         }
367         sd_bus_message_unref(ret);
368         return rc;
369 }
370
371 static int unit_restart(struct sd_bus *bus, const char *dpath)
372 {
373         int rc;
374         struct sd_bus_message *ret = NULL;
375         sd_bus_error err = SD_BUS_ERROR_NULL;
376
377         rc = sd_bus_call_method(bus, sdb_destination, dpath, sdbi_unit, sdbm_restart, &err, &ret, "s", "replace");
378         if(ret) {
379                 rc = job_wait(bus, ret);
380         }
381         sd_bus_message_unref(ret);
382         return rc;
383 }
384
385 static int unit_stop(struct sd_bus *bus, const char *dpath)
386 {
387         int rc;
388         struct sd_bus_message *ret = NULL;
389         sd_bus_error err = SD_BUS_ERROR_NULL;
390
391         rc = sd_bus_call_method(bus, sdb_destination, dpath, sdbi_unit, sdbm_stop, &err, &ret, "s", "replace");
392         sd_bus_message_unref(ret);
393         return rc;
394 }
395
396 static int unit_start_name(struct sd_bus *bus, const char *name)
397 {
398         int rc;
399         struct sd_bus_message *ret = NULL;
400         sd_bus_error err = SD_BUS_ERROR_NULL;
401
402         rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, sdbm_start_unit, &err, &ret, "ss", name, "replace");
403         if(ret) {
404                 rc = job_wait(bus, ret);
405         }
406         sd_bus_message_unref(ret);
407         return rc;
408 }
409
410 static int unit_restart_name(struct sd_bus *bus, const char *name)
411 {
412         int rc;
413         struct sd_bus_message *ret = NULL;
414         sd_bus_error err = SD_BUS_ERROR_NULL;
415
416         rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, sdbm_restart_unit, &err, &ret, "ss", name, "replace");
417         if(ret) {
418                 rc = job_wait(bus, ret);
419         }
420         sd_bus_message_unref(ret);
421         return rc;
422 }
423
424 static int unit_stop_name(struct sd_bus *bus, const char *name)
425 {
426         int rc;
427         struct sd_bus_message *ret = NULL;
428         sd_bus_error err = SD_BUS_ERROR_NULL;
429
430         rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, sdbm_stop_unit, &err, &ret, "ss", name, "replace");
431         sd_bus_message_unref(ret);
432         return rc;
433 }
434
435 /********************************************************************
436  *
437  *******************************************************************/
438
439 static int check_snprintf_result(int rc, size_t buflen)
440 {
441         return (rc >= 0 && (size_t)rc >= buflen) ? seterrno(ENAMETOOLONG) : rc;
442 }
443
444 int systemd_get_units_dir(char *path, size_t pathlen, int isuser)
445 {
446         int rc = snprintf(path, pathlen, "%s/%s",
447                         SYSTEMD_UNITS_ROOT,
448                         isuser ? "user" : "system");
449
450         return check_snprintf_result(rc, pathlen);
451 }
452
453 int systemd_get_unit_path(char *path, size_t pathlen, int isuser, const char *unit, const char *uext)
454 {
455         int rc = snprintf(path, pathlen, "%s/%s/%s.%s",
456                         SYSTEMD_UNITS_ROOT,
457                         isuser ? "user" : "system",
458                         unit,
459                         uext);
460
461         return check_snprintf_result(rc, pathlen);
462 }
463
464 int systemd_get_wants_path(char *path, size_t pathlen, int isuser, const char *wanter, const char *unit, const char *uext)
465 {
466         int rc = snprintf(path, pathlen, "%s/%s/%s.wants/%s.%s",
467                         SYSTEMD_UNITS_ROOT,
468                         isuser ? "user" : "system",
469                         wanter,
470                         unit,
471                         uext);
472
473         return check_snprintf_result(rc, pathlen);
474 }
475
476 int systemd_get_wants_target(char *path, size_t pathlen, const char *unit, const char *uext)
477 {
478         int rc = snprintf(path, pathlen, "../%s.%s", unit, uext);
479
480         return check_snprintf_result(rc, pathlen);
481 }
482
483 int systemd_daemon_reload(int isuser)
484 {
485         int rc;
486         struct sd_bus *bus;
487         struct sd_bus_message *ret = NULL;
488         sd_bus_error err = SD_BUS_ERROR_NULL;
489
490         rc = systemd_get_bus(isuser, &bus);
491         if (rc >= 0) {
492                 /* TODO: asynchronous bind... */
493                 /* TODO: more diagnostic... */
494                 rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, sdbm_reload, &err, &ret, NULL);
495                 sd_bus_message_unref(ret);
496         }
497         return rc;
498 }
499
500 int systemd_unit_list(int isuser, int (*callback)(void *closure, const char *name, const char *path, int isuser), void *closure)
501 {
502         DIR *dir;
503         char path[PATH_MAX + 1];
504         struct dirent *dent;
505         int rc, isfile;
506         size_t offset, len;
507         struct stat st;
508
509         /* get the path */
510         rc = systemd_get_units_dir(path, sizeof path - 1, isuser);
511         if (rc < 0)
512                 return rc;
513         offset = (size_t)rc;
514
515         /* open the directory */
516         dir = opendir(path);
517         if (!dir)
518                 return -1;
519
520         /* prepare path */
521         path[offset++] = '/';
522
523         /* read the directory */
524         for(;;) {
525                 /* get next entry */
526                 errno = 0;
527                 dent = readdir(dir);
528                 if (dent == NULL) {
529                         /* end or error */
530                         rc = (!errno) - 1;
531                         break;
532                 }
533
534                 /* is a file? */
535                 if (dent->d_type == DT_REG)
536                         isfile = 1;
537                 else if (dent->d_type != DT_UNKNOWN)
538                         isfile = 0;
539                 else {
540                         rc = fstatat(dirfd(dir), dent->d_name, &st, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT);
541                         if (rc < 0)
542                                 break;
543                         isfile = S_ISREG(st.st_mode);
544                 }
545
546                 /* calls the callback if is a file */
547                 if (isfile) {
548                         len = strlen(dent->d_name);
549                         if (offset + len >= sizeof path) {
550                                 rc = seterrno(ENAMETOOLONG);
551                                 break;
552                         }
553                         memcpy(&path[offset], dent->d_name, 1 + len);
554                         rc = callback(closure, &path[offset], path, isuser);
555                         if (rc)
556                                 break;
557                 }
558         }
559         closedir(dir);
560         return rc;
561 }
562
563 int systemd_unit_list_all(int (*callback)(void *closure, const char *name, const char *path, int isuser), void *closure)
564 {
565         return systemd_unit_list(1, callback, closure) ? : systemd_unit_list(0, callback, closure);
566 }
567
568 char *systemd_unit_dpath_by_name(int isuser, const char *name, int load)
569 {
570         struct sd_bus *bus;
571
572         return systemd_get_bus(isuser, &bus) < 0 ? NULL : get_unit_dpath(bus, name, load);
573 }
574
575 char *systemd_unit_dpath_by_pid(int isuser, unsigned pid)
576 {
577         struct sd_bus *bus;
578
579         return systemd_get_bus(isuser, &bus) < 0 ? NULL : get_unit_dpath_by_pid(bus, pid);
580 }
581
582 int systemd_unit_start_dpath(int isuser, const char *dpath)
583 {
584         int rc;
585         struct sd_bus *bus;
586
587         rc = systemd_get_bus(isuser, &bus);
588         return rc < 0 ? rc : unit_start(bus, dpath);
589 }
590
591 int systemd_unit_restart_dpath(int isuser, const char *dpath)
592 {
593         int rc;
594         struct sd_bus *bus;
595
596         rc = systemd_get_bus(isuser, &bus);
597         return rc < 0 ? rc : unit_restart(bus, dpath);
598 }
599
600 int systemd_unit_stop_dpath(int isuser, const char *dpath)
601 {
602         int rc;
603         struct sd_bus *bus;
604
605         rc = systemd_get_bus(isuser, &bus);
606         return rc < 0 ? rc : unit_stop(bus, dpath);
607 }
608
609 int systemd_unit_start_name(int isuser, const char *name)
610 {
611         int rc;
612         struct sd_bus *bus;
613
614         rc = systemd_get_bus(isuser, &bus);
615         if (rc >= 0)
616                 rc = unit_start_name(bus, name);
617         return rc;
618 }
619
620 int systemd_unit_restart_name(int isuser, const char *name)
621 {
622         int rc;
623         struct sd_bus *bus;
624
625         rc = systemd_get_bus(isuser, &bus);
626         if (rc >= 0)
627                 rc = unit_restart_name(bus, name);
628         return rc;
629 }
630
631 int systemd_unit_stop_name(int isuser, const char *name)
632 {
633         int rc;
634         struct sd_bus *bus;
635
636         rc = systemd_get_bus(isuser, &bus);
637         if (rc >= 0)
638                 rc = unit_stop_name(bus, name);
639         return rc;
640 }
641
642 int systemd_unit_stop_pid(int isuser, unsigned pid)
643 {
644         int rc;
645         struct sd_bus *bus;
646         char *dpath;
647
648         rc = systemd_get_bus(isuser, &bus);
649         if (rc >= 0) {
650                 dpath = get_unit_dpath_by_pid(bus, pid);
651                 if (!dpath)
652                         rc = -1;
653                 else {
654                         rc = unit_stop(bus, dpath);
655                         free(dpath);
656                 }
657         }
658         return rc;
659 }
660
661 int systemd_unit_pid_of_dpath(int isuser, const char *dpath)
662 {
663         int rc;
664         struct sd_bus *bus;
665
666         rc = systemd_get_bus(isuser, &bus);
667         return rc < 0 ? rc : unit_pid(bus, dpath);
668 }
669
670 enum SysD_State systemd_unit_state_of_dpath(int isuser, const char *dpath)
671 {
672         int rc;
673         struct sd_bus *bus;
674
675         rc = systemd_get_bus(isuser, &bus);
676         return rc < 0 ? SysD_State_INVALID : unit_state(bus, dpath);
677 }
678
679 const char *systemd_state_name(enum SysD_State state)
680 {
681         return sds_state_names[state];
682 }