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