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