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