60ec4bfa64e9a2595409d29bc7e11bd61fd2528d
[project/rpcd.git] / rc.c
1 // SPDX-License-Identifier: ISC OR MIT
2 /*
3 * rpcd - UBUS RPC server
4 *
5 * Copyright (C) 2020 Rafał Miłecki <rafal@milecki.pl>
6 */
7
8 #include <dirent.h>
9 #include <fcntl.h>
10 #include <linux/limits.h>
11 #include <sys/stat.h>
12 #include <sys/wait.h>
13
14 #include <libubox/blobmsg.h>
15 #include <libubox/ulog.h>
16 #include <libubox/uloop.h>
17 #include <libubus.h>
18
19 #include <rpcd/rc.h>
20
21 #define RC_LIST_EXEC_TIMEOUT_MS 3000
22
23 enum {
24 RC_LIST_SKIP_RUNNING_CHECK,
25 __RC_LIST_MAX
26 };
27
28 static const struct blobmsg_policy rc_list_policy[] = {
29 [RC_LIST_SKIP_RUNNING_CHECK] = { "skip_running_check", BLOBMSG_TYPE_BOOL },
30 };
31
32 enum {
33 RC_INIT_NAME,
34 RC_INIT_ACTION,
35 __RC_INIT_MAX
36 };
37
38 static const struct blobmsg_policy rc_init_policy[] = {
39 [RC_INIT_NAME] = { "name", BLOBMSG_TYPE_STRING },
40 [RC_INIT_ACTION] = { "action", BLOBMSG_TYPE_STRING },
41 };
42
43 struct rc_list_context {
44 struct uloop_process process;
45 struct uloop_timeout timeout;
46 struct ubus_context *ctx;
47 struct ubus_request_data req;
48 struct blob_buf *buf;
49 DIR *dir;
50 bool skip_running_check;
51
52 /* Info about currently processed init.d entry */
53 struct {
54 char path[PATH_MAX];
55 const char *d_name;
56 int start;
57 int stop;
58 bool enabled;
59 bool running;
60 bool use_procd;
61 } entry;
62 };
63
64 static void rc_list_readdir(struct rc_list_context *c);
65
66 /**
67 * rc_check_script - check if script is safe to execute as root
68 *
69 * Check if it's owned by root and if only root can modify it.
70 */
71 static int rc_check_script(const char *path)
72 {
73 struct stat s;
74
75 if (stat(path, &s))
76 return UBUS_STATUS_NOT_FOUND;
77
78 if (s.st_uid != 0 || s.st_gid != 0 || !(s.st_mode & S_IXUSR) || (s.st_mode & S_IWOTH))
79 return UBUS_STATUS_PERMISSION_DENIED;
80
81 return UBUS_STATUS_OK;
82 }
83
84 static void rc_list_add_table(struct rc_list_context *c)
85 {
86 void *e;
87
88 e = blobmsg_open_table(c->buf, c->entry.d_name);
89
90 if (c->entry.start >= 0)
91 blobmsg_add_u16(c->buf, "start", c->entry.start);
92 if (c->entry.stop >= 0)
93 blobmsg_add_u16(c->buf, "stop", c->entry.stop);
94 blobmsg_add_u8(c->buf, "enabled", c->entry.enabled);
95 if (!c->skip_running_check && c->entry.use_procd)
96 blobmsg_add_u8(c->buf, "running", c->entry.running);
97
98 blobmsg_close_table(c->buf, e);
99 }
100
101 static void rpc_list_exec_timeout_cb(struct uloop_timeout *t)
102 {
103 struct rc_list_context *c = container_of(t, struct rc_list_context, timeout);
104
105 ULOG_WARN("Timeout waiting for %s\n", c->entry.path);
106
107 uloop_process_delete(&c->process);
108 kill(c->process.pid, SIGKILL);
109
110 rc_list_readdir(c);
111 }
112
113 /**
114 * rc_exec - execute a file and call callback on complete
115 */
116 static int rc_list_exec(struct rc_list_context *c, const char *action, uloop_process_handler cb)
117 {
118 pid_t pid;
119 int err;
120 int fd;
121
122 pid = fork();
123 switch (pid) {
124 case -1:
125 return -errno;
126 case 0:
127 if (c->skip_running_check)
128 exit(-EFAULT);
129
130 if (!c->entry.use_procd)
131 exit(-EOPNOTSUPP);
132
133 /* Set stdin, stdout & stderr to /dev/null */
134 fd = open("/dev/null", O_RDWR);
135 if (fd >= 0) {
136 dup2(fd, 0);
137 dup2(fd, 1);
138 dup2(fd, 2);
139 if (fd > 2)
140 close(fd);
141 }
142
143 uloop_end();
144
145 execl(c->entry.path, c->entry.path, action, NULL);
146 exit(errno);
147 default:
148 c->process.pid = pid;
149 c->process.cb = cb;
150
151 err = uloop_process_add(&c->process);
152 if (err)
153 return err;
154
155 c->timeout.cb = rpc_list_exec_timeout_cb;
156 err = uloop_timeout_set(&c->timeout, RC_LIST_EXEC_TIMEOUT_MS);
157 if (err) {
158 uloop_process_delete(&c->process);
159 return err;
160 }
161
162 return 0;
163 }
164 }
165
166 static void rc_list_exec_running_cb(struct uloop_process *p, int stat)
167 {
168 struct rc_list_context *c = container_of(p, struct rc_list_context, process);
169
170 uloop_timeout_cancel(&c->timeout);
171
172 c->entry.running = !stat;
173 rc_list_add_table(c);
174
175 rc_list_readdir(c);
176 }
177
178 static void rc_list_readdir(struct rc_list_context *c)
179 {
180 struct dirent *e;
181 FILE *fp;
182
183 e = readdir(c->dir);
184 if (!e) {
185 closedir(c->dir);
186 ubus_send_reply(c->ctx, &c->req, c->buf->head);
187 ubus_complete_deferred_request(c->ctx, &c->req, UBUS_STATUS_OK);
188 return;
189 }
190
191 if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
192 goto next;
193
194 memset(&c->entry, 0, sizeof(c->entry));
195 c->entry.start = -1;
196 c->entry.stop = -1;
197
198 snprintf(c->entry.path, sizeof(c->entry.path), "/etc/init.d/%s", e->d_name);
199 if (rc_check_script(c->entry.path))
200 goto next;
201
202 c->entry.d_name = e->d_name;
203
204 fp = fopen(c->entry.path, "r");
205 if (fp) {
206 struct stat s;
207 char path[PATH_MAX];
208 char line[255];
209 bool beginning;
210 int count = 0;
211
212 beginning = true;
213 while ((c->entry.start < 0 || c->entry.stop < 0 ||
214 (!c->skip_running_check && !c->entry.use_procd)) &&
215 count <= 10 && fgets(line, sizeof(line), fp)) {
216 if (beginning) {
217 if (!strncmp(line, "START=", 6)) {
218 c->entry.start = strtoul(line + 6, NULL, 0);
219 } else if (!strncmp(line, "STOP=", 5)) {
220 c->entry.stop = strtoul(line + 5, NULL, 0);
221 } else if (!c->skip_running_check && !strncmp(line, "USE_PROCD=", 10)) {
222 c->entry.use_procd = !!strtoul(line + 10, NULL, 0);
223 }
224 count++;
225 }
226
227 beginning = !!strchr(line, '\n');
228 }
229 fclose(fp);
230
231 if (c->entry.start >= 0) {
232 snprintf(path, sizeof(path), "/etc/rc.d/S%02d%s", c->entry.start, c->entry.d_name);
233 if (!stat(path, &s) && (s.st_mode & S_IXUSR))
234 c->entry.enabled = true;
235 }
236 }
237
238 if (rc_list_exec(c, "running", rc_list_exec_running_cb))
239 goto next;
240
241 return;
242 next:
243 rc_list_readdir(c);
244 }
245
246 /**
247 * rc_list - allocate listing context and start reading directory
248 */
249 static int rc_list(struct ubus_context *ctx, struct ubus_object *obj,
250 struct ubus_request_data *req, const char *method,
251 struct blob_attr *msg)
252 {
253 struct blob_attr *tb[__RC_LIST_MAX];
254 static struct blob_buf buf;
255 struct rc_list_context *c;
256
257 blobmsg_parse(rc_list_policy, __RC_LIST_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
258
259 blob_buf_init(&buf, 0);
260
261 c = calloc(1, sizeof(*c));
262 if (!c)
263 return UBUS_STATUS_UNKNOWN_ERROR;
264
265 c->ctx = ctx;
266 c->buf = &buf;
267 c->dir = opendir("/etc/init.d");
268 if (!c->dir) {
269 free(c);
270 return UBUS_STATUS_UNKNOWN_ERROR;
271 }
272 if (tb[RC_LIST_SKIP_RUNNING_CHECK])
273 c->skip_running_check = blobmsg_get_bool(tb[RC_LIST_SKIP_RUNNING_CHECK]);
274
275 ubus_defer_request(ctx, req, &c->req);
276
277 rc_list_readdir(c);
278
279 return 0; /* Deferred */
280 }
281
282 struct rc_init_context {
283 struct uloop_process process;
284 struct ubus_context *ctx;
285 struct ubus_request_data req;
286 };
287
288 static void rc_init_cb(struct uloop_process *p, int stat)
289 {
290 struct rc_init_context *c = container_of(p, struct rc_init_context, process);
291
292 ubus_complete_deferred_request(c->ctx, &c->req, UBUS_STATUS_OK);
293
294 free(c);
295 }
296
297 static int rc_init(struct ubus_context *ctx, struct ubus_object *obj,
298 struct ubus_request_data *req, const char *method,
299 struct blob_attr *msg)
300 {
301 struct blob_attr *tb[__RC_INIT_MAX];
302 struct rc_init_context *c;
303 char path[PATH_MAX];
304 const char *action;
305 const char *name;
306 const char *chr;
307 pid_t pid;
308 int err;
309 int fd;
310
311 blobmsg_parse(rc_init_policy, __RC_INIT_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
312
313 if (!tb[RC_INIT_NAME] || !tb[RC_INIT_ACTION])
314 return UBUS_STATUS_INVALID_ARGUMENT;
315
316 name = blobmsg_get_string(tb[RC_INIT_NAME]);
317
318 /* Validate script name */
319 for (chr = name; (chr = strchr(chr, '.')); chr++) {
320 if (*(chr + 1) == '.')
321 return UBUS_STATUS_INVALID_ARGUMENT;
322 }
323 if (strchr(name, '/'))
324 return UBUS_STATUS_INVALID_ARGUMENT;
325
326 snprintf(path, sizeof(path), "/etc/init.d/%s", name);
327
328 /* Validate script privileges */
329 err = rc_check_script(path);
330 if (err)
331 return err;
332
333 action = blobmsg_get_string(tb[RC_INIT_ACTION]);
334 if (strcmp(action, "disable") &&
335 strcmp(action, "enable") &&
336 strcmp(action, "stop") &&
337 strcmp(action, "start") &&
338 strcmp(action, "restart") &&
339 strcmp(action, "reload"))
340 return UBUS_STATUS_INVALID_ARGUMENT;
341
342 c = calloc(1, sizeof(*c));
343 if (!c)
344 return UBUS_STATUS_UNKNOWN_ERROR;
345
346 pid = fork();
347 switch (pid) {
348 case -1:
349 free(c);
350 return UBUS_STATUS_UNKNOWN_ERROR;
351 case 0:
352 /* Set stdin, stdout & stderr to /dev/null */
353 fd = open("/dev/null", O_RDWR);
354 if (fd >= 0) {
355 dup2(fd, 0);
356 dup2(fd, 1);
357 dup2(fd, 2);
358 if (fd > 2)
359 close(fd);
360 }
361
362 uloop_end();
363
364 execl(path, path, action, NULL);
365 exit(errno);
366 default:
367 c->ctx = ctx;
368 c->process.pid = pid;
369 c->process.cb = rc_init_cb;
370 uloop_process_add(&c->process);
371
372 ubus_defer_request(ctx, req, &c->req);
373
374 return 0; /* Deferred */
375 }
376 }
377
378 int rpc_rc_api_init(struct ubus_context *ctx)
379 {
380 static const struct ubus_method rc_methods[] = {
381 UBUS_METHOD("list", rc_list, rc_list_policy),
382 UBUS_METHOD("init", rc_init, rc_init_policy),
383 };
384
385 static struct ubus_object_type rc_type =
386 UBUS_OBJECT_TYPE("rc", rc_methods);
387
388 static struct ubus_object obj = {
389 .name = "rc",
390 .type = &rc_type,
391 .methods = rc_methods,
392 .n_methods = ARRAY_SIZE(rc_methods),
393 };
394
395 return ubus_add_object(ctx, &obj);
396 }