• source navigation  • diff markup  • identifier search  • freetext search  • 

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt