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