b8c3c0bd9fc2b75d3471cc750f2c029e6d58e4d8
[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 #include <systemd/sd-bus.h>
31 #include <systemd/sd-bus-protocol.h>
32
33 #include "utils-systemd.h"
34
35 #if !defined(SYSTEMD_UNITS_ROOT)
36 # define SYSTEMD_UNITS_ROOT "/usr/local/lib/systemd"
37 #endif
38
39 static const char sdb_path[] = "/org/freedesktop/systemd1";
40 static const char sdb_destination[] = "org.freedesktop.systemd1";
41 static const char sdbi_manager[] = "org.freedesktop.systemd1.Manager";
42 static const char sdbm_reload[] = "Reload";
43
44 static struct sd_bus *sysbus;
45 static struct sd_bus *usrbus;
46
47 /*
48  * Translate systemd errors to errno errors
49  */
50 static int seterrno(int value)
51 {
52         errno = value;
53         return -1;
54 }
55
56 static int sderr2errno(int rc)
57 {
58         return rc < 0 ? seterrno(-rc) : rc;
59 }
60
61 static int errno2sderr(int rc)
62 {
63         return rc < 0 ? -errno : rc;
64 }
65
66 /* Returns in 'ret' either the system bus (if isuser==0)
67  * or the user bus (if isuser!=0).
68  * Returns 0 in case of success or -1 in case of error
69  */
70 static int get_bus(int isuser, struct sd_bus **ret)
71 {
72         int rc;
73         struct sd_bus *bus;
74
75         bus = isuser ? usrbus : sysbus;
76         if (bus) {
77                 *ret = bus;
78                 rc = 0;
79         } else if (isuser) {
80                 rc = sd_bus_default_user(ret);
81                 if (!rc)
82                         usrbus = *ret;
83         } else {
84                 rc = sd_bus_default_system(ret);
85                 if (!rc)
86                         sysbus = *ret;
87         }
88         return sderr2errno(rc);
89 }
90 static int check_snprintf_result(int rc, size_t buflen)
91 {
92         return (rc >= 0 && (size_t)rc >= buflen) ? seterrno(ENAMETOOLONG) : rc;
93 }
94
95 int systemd_get_units_dir(char *path, size_t pathlen, int isuser)
96 {
97         int rc = snprintf(path, pathlen, "%s/%s", 
98                         SYSTEMD_UNITS_ROOT,
99                         isuser ? "user" : "system");
100
101         return check_snprintf_result(rc, pathlen);
102 }
103
104 int systemd_get_unit_path(char *path, size_t pathlen, int isuser, const char *unit, const char *uext)
105 {
106         int rc = snprintf(path, pathlen, "%s/%s/%s.%s", 
107                         SYSTEMD_UNITS_ROOT,
108                         isuser ? "user" : "system",
109                         unit,
110                         uext);
111
112         return check_snprintf_result(rc, pathlen);
113 }
114
115 int systemd_get_wants_path(char *path, size_t pathlen, int isuser, const char *wanter, const char *unit, const char *uext)
116 {
117         int rc = snprintf(path, pathlen, "%s/%s/%s.wants/%s.%s", 
118                         SYSTEMD_UNITS_ROOT,
119                         isuser ? "user" : "system",
120                         wanter,
121                         unit,
122                         uext);
123
124         return check_snprintf_result(rc, pathlen);
125 }
126
127 int systemd_get_wants_target(char *path, size_t pathlen, const char *unit, const char *uext)
128 {
129         int rc = snprintf(path, pathlen, "../%s.%s", unit, uext);
130
131         return check_snprintf_result(rc, pathlen);
132 }
133
134 int systemd_daemon_reload(int isuser)
135 {
136         int rc;
137         struct sd_bus *bus;
138
139         rc = get_bus(isuser, &bus);
140         if (!rc) {
141                 /* TODO: asynchronous bind... */
142                 /* TODO: more diagnostic... */
143                 rc = sd_bus_call_method(bus, sdb_destination, sdb_path, sdbi_manager, sdbm_reload, NULL, NULL, NULL);
144         }
145         return rc;
146 }
147
148 int systemd_unit_list(int isuser, int (*callback)(void *closure, const char *name, const char *path, int isuser), void *closure)
149 {
150         DIR *dir;
151         char path[PATH_MAX + 1];
152         struct dirent *dent;
153         int rc, isfile;
154         size_t offset, len;
155         struct stat st;
156
157         /* get the path */
158         rc = systemd_get_units_dir(path, sizeof path - 1, isuser);
159         if (rc < 0)
160                 return rc;
161         offset = (size_t)rc;
162
163         /* open the directory */
164         dir = opendir(path);
165         if (!dir)
166                 return -1;
167
168         /* prepare path */
169         path[offset++] = '/';
170
171         /* read the directory */
172         for(;;) {
173                 /* get next entry */
174                 errno = 0;
175                 dent = readdir(dir);
176                 if (dent == NULL) {
177                         /* end or error */
178                         rc = (!errno) - 1;
179                         break;
180                 }
181
182                 /* is a file? */
183                 if (dent->d_type == DT_REG)
184                         isfile = 1;
185                 else if (dent->d_type != DT_UNKNOWN)
186                         isfile = 0;
187                 else {
188                         rc = fstatat(dirfd(dir), dent->d_name, &st, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT);
189                         if (rc < 0)
190                                 break;
191                         isfile = S_ISREG(st.st_mode);
192                 }
193
194                 /* calls the callback if is a file */
195                 if (isfile) {
196                         len = strlen(dent->d_name);
197                         if (offset + len >= sizeof path) {
198                                 rc = seterrno(ENAMETOOLONG);
199                                 break;
200                         }
201                         memcpy(&path[offset], dent->d_name, 1 + len);
202                         rc = callback(closure, &path[offset], path, isuser);
203                         if (rc)
204                                 break;
205                 }
206         }
207         closedir(dir);
208         return rc;
209 }
210
211 int systemd_unit_list_all(int (*callback)(void *closure, const char *name, const char *path, int isuser), void *closure)
212 {
213         return systemd_unit_list(1, callback, closure) ? : systemd_unit_list(0, callback, closure);
214 }
215