instance: add support for customizable syslog facilities
[oweals/procd.git] / system.c
1 /*
2  * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3  * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License version 2.1
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <sys/utsname.h>
16 #ifdef linux
17 #include <sys/sysinfo.h>
18 #endif
19 #include <sys/ioctl.h>
20 #include <sys/types.h>
21 #include <sys/reboot.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <signal.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27
28 #include <libubox/uloop.h>
29
30 #include "procd.h"
31 #include "sysupgrade.h"
32 #include "watchdog.h"
33
34 static struct blob_buf b;
35 static int notify;
36 static struct ubus_context *_ctx;
37
38 static int system_board(struct ubus_context *ctx, struct ubus_object *obj,
39                  struct ubus_request_data *req, const char *method,
40                  struct blob_attr *msg)
41 {
42         void *c;
43         char line[256];
44         char *key, *val, *next;
45         struct utsname utsname;
46         FILE *f;
47
48         blob_buf_init(&b, 0);
49
50         if (uname(&utsname) >= 0)
51         {
52                 blobmsg_add_string(&b, "kernel", utsname.release);
53                 blobmsg_add_string(&b, "hostname", utsname.nodename);
54         }
55
56         if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
57         {
58                 while(fgets(line, sizeof(line), f))
59                 {
60                         key = strtok(line, "\t:");
61                         val = strtok(NULL, "\t\n");
62
63                         if (!key || !val)
64                                 continue;
65
66                         if (!strcasecmp(key, "system type") ||
67                             !strcasecmp(key, "processor") ||
68                             !strcasecmp(key, "cpu") ||
69                             !strcasecmp(key, "model name"))
70                         {
71                                 strtoul(val + 2, &key, 0);
72
73                                 if (key == (val + 2) || *key != 0)
74                                 {
75                                         blobmsg_add_string(&b, "system", val + 2);
76                                         break;
77                                 }
78                         }
79                 }
80
81                 fclose(f);
82         }
83
84         if ((f = fopen("/tmp/sysinfo/model", "r")) != NULL ||
85             (f = fopen("/proc/device-tree/model", "r")) != NULL)
86         {
87                 if (fgets(line, sizeof(line), f))
88                 {
89                         val = strtok(line, "\t\n");
90
91                         if (val)
92                                 blobmsg_add_string(&b, "model", val);
93                 }
94
95                 fclose(f);
96         }
97         else if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
98         {
99                 while(fgets(line, sizeof(line), f))
100                 {
101                         key = strtok(line, "\t:");
102                         val = strtok(NULL, "\t\n");
103
104                         if (!key || !val)
105                                 continue;
106
107                         if (!strcasecmp(key, "machine") ||
108                             !strcasecmp(key, "hardware"))
109                         {
110                                 blobmsg_add_string(&b, "model", val + 2);
111                                 break;
112                         }
113                 }
114
115                 fclose(f);
116         }
117
118         if ((f = fopen("/tmp/sysinfo/board_name", "r")) != NULL)
119         {
120                 if (fgets(line, sizeof(line), f))
121                 {
122                         val = strtok(line, "\t\n");
123
124                         if (val)
125                                 blobmsg_add_string(&b, "board_name", val);
126                 }
127
128                 fclose(f);
129         }
130         else if ((f = fopen("/proc/device-tree/compatible", "r")) != NULL)
131         {
132                 if (fgets(line, sizeof(line), f))
133                 {
134                         val = strtok(line, "\t\n");
135
136                         if (val)
137                         {
138                                 next = val;
139                                 while ((next = strchr(next, ',')) != NULL)
140                                 {
141                                         *next = '-';
142                                         next++;
143                                 }
144
145                                 blobmsg_add_string(&b, "board_name", val);
146                         }
147                 }
148
149                 fclose(f);
150         }
151
152         if ((f = fopen("/etc/openwrt_release", "r")) != NULL)
153         {
154                 c = blobmsg_open_table(&b, "release");
155
156                 while (fgets(line, sizeof(line), f))
157                 {
158                         char *dest;
159                         char ch;
160
161                         key = line;
162                         val = strchr(line, '=');
163                         if (!val)
164                                 continue;
165
166                         *(val++) = 0;
167
168                         if (!strcasecmp(key, "DISTRIB_ID"))
169                                 key = "distribution";
170                         else if (!strcasecmp(key, "DISTRIB_RELEASE"))
171                                 key = "version";
172                         else if (!strcasecmp(key, "DISTRIB_REVISION"))
173                                 key = "revision";
174                         else if (!strcasecmp(key, "DISTRIB_CODENAME"))
175                                 key = "codename";
176                         else if (!strcasecmp(key, "DISTRIB_TARGET"))
177                                 key = "target";
178                         else if (!strcasecmp(key, "DISTRIB_DESCRIPTION"))
179                                 key = "description";
180                         else
181                                 continue;
182
183                         dest = blobmsg_alloc_string_buffer(&b, key, strlen(val));
184                         if (!dest) {
185                                 ERROR("Failed to allocate blob.\n");
186                                 continue;
187                         }
188
189                         while (val && (ch = *(val++)) != 0) {
190                                 switch (ch) {
191                                 case '\'':
192                                 case '"':
193                                         next = strchr(val, ch);
194                                         if (next)
195                                                 *next = 0;
196
197                                         strcpy(dest, val);
198
199                                         if (next)
200                                                 val = next + 1;
201
202                                         dest += strlen(dest);
203                                         break;
204                                 case '\\':
205                                         *(dest++) = *(val++);
206                                         break;
207                                 }
208                         }
209                         blobmsg_add_string_buffer(&b);
210                 }
211
212                 blobmsg_close_array(&b, c);
213
214                 fclose(f);
215         }
216
217         ubus_send_reply(ctx, req, b.head);
218
219         return UBUS_STATUS_OK;
220 }
221
222 static int system_info(struct ubus_context *ctx, struct ubus_object *obj,
223                 struct ubus_request_data *req, const char *method,
224                 struct blob_attr *msg)
225 {
226         time_t now;
227         struct tm *tm;
228 #ifdef linux
229         struct sysinfo info;
230         void *c;
231
232         if (sysinfo(&info))
233                 return UBUS_STATUS_UNKNOWN_ERROR;
234 #endif
235
236         now = time(NULL);
237
238         if (!(tm = localtime(&now)))
239                 return UBUS_STATUS_UNKNOWN_ERROR;
240
241         blob_buf_init(&b, 0);
242
243         blobmsg_add_u32(&b, "localtime", now + tm->tm_gmtoff);
244
245 #ifdef linux
246         blobmsg_add_u32(&b, "uptime",    info.uptime);
247
248         c = blobmsg_open_array(&b, "load");
249         blobmsg_add_u32(&b, NULL, info.loads[0]);
250         blobmsg_add_u32(&b, NULL, info.loads[1]);
251         blobmsg_add_u32(&b, NULL, info.loads[2]);
252         blobmsg_close_array(&b, c);
253
254         c = blobmsg_open_table(&b, "memory");
255         blobmsg_add_u64(&b, "total",    info.mem_unit * info.totalram);
256         blobmsg_add_u64(&b, "free",     info.mem_unit * info.freeram);
257         blobmsg_add_u64(&b, "shared",   info.mem_unit * info.sharedram);
258         blobmsg_add_u64(&b, "buffered", info.mem_unit * info.bufferram);
259         blobmsg_close_table(&b, c);
260
261         c = blobmsg_open_table(&b, "swap");
262         blobmsg_add_u64(&b, "total",    info.mem_unit * info.totalswap);
263         blobmsg_add_u64(&b, "free",     info.mem_unit * info.freeswap);
264         blobmsg_close_table(&b, c);
265 #endif
266
267         ubus_send_reply(ctx, req, b.head);
268
269         return UBUS_STATUS_OK;
270 }
271
272 static int system_reboot(struct ubus_context *ctx, struct ubus_object *obj,
273                          struct ubus_request_data *req, const char *method,
274                          struct blob_attr *msg)
275 {
276         procd_shutdown(RB_AUTOBOOT);
277         return 0;
278 }
279
280 enum {
281         WDT_FREQUENCY,
282         WDT_TIMEOUT,
283         WDT_MAGICCLOSE,
284         WDT_STOP,
285         __WDT_MAX
286 };
287
288 static const struct blobmsg_policy watchdog_policy[__WDT_MAX] = {
289         [WDT_FREQUENCY] = { .name = "frequency", .type = BLOBMSG_TYPE_INT32 },
290         [WDT_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 },
291         [WDT_MAGICCLOSE] = { .name = "magicclose", .type = BLOBMSG_TYPE_BOOL },
292         [WDT_STOP] = { .name = "stop", .type = BLOBMSG_TYPE_BOOL },
293 };
294
295 static int watchdog_set(struct ubus_context *ctx, struct ubus_object *obj,
296                         struct ubus_request_data *req, const char *method,
297                         struct blob_attr *msg)
298 {
299         struct blob_attr *tb[__WDT_MAX];
300         const char *status;
301
302         if (!msg)
303                 return UBUS_STATUS_INVALID_ARGUMENT;
304
305         blobmsg_parse(watchdog_policy, __WDT_MAX, tb, blob_data(msg), blob_len(msg));
306         if (tb[WDT_FREQUENCY]) {
307                 unsigned int timeout = tb[WDT_TIMEOUT] ? blobmsg_get_u32(tb[WDT_TIMEOUT]) :
308                                                 watchdog_timeout(0);
309                 unsigned int freq = blobmsg_get_u32(tb[WDT_FREQUENCY]);
310
311                 if (freq) {
312                         if (freq > timeout / 2)
313                                 freq = timeout / 2;
314                         watchdog_frequency(freq);
315                 }
316         }
317
318         if (tb[WDT_TIMEOUT]) {
319                 unsigned int timeout = blobmsg_get_u32(tb[WDT_TIMEOUT]);
320                 unsigned int frequency = watchdog_frequency(0);
321
322                 if (timeout <= frequency)
323                         timeout = frequency * 2;
324                  watchdog_timeout(timeout);
325         }
326
327         if (tb[WDT_MAGICCLOSE])
328                 watchdog_set_magicclose(blobmsg_get_bool(tb[WDT_MAGICCLOSE]));
329
330         if (tb[WDT_STOP])
331                 watchdog_set_stopped(blobmsg_get_bool(tb[WDT_STOP]));
332
333         if (watchdog_fd() == NULL)
334                 status = "offline";
335         else if (watchdog_get_stopped())
336                 status = "stopped";
337         else
338                 status = "running";
339
340         blob_buf_init(&b, 0);
341         blobmsg_add_string(&b, "status", status);
342         blobmsg_add_u32(&b, "timeout", watchdog_timeout(0));
343         blobmsg_add_u32(&b, "frequency", watchdog_frequency(0));
344         blobmsg_add_u8(&b, "magicclose", watchdog_get_magicclose());
345         ubus_send_reply(ctx, req, b.head);
346
347         return 0;
348 }
349
350 enum {
351         SIGNAL_PID,
352         SIGNAL_NUM,
353         __SIGNAL_MAX
354 };
355
356 static const struct blobmsg_policy signal_policy[__SIGNAL_MAX] = {
357         [SIGNAL_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
358         [SIGNAL_NUM] = { .name = "signum", .type = BLOBMSG_TYPE_INT32 },
359 };
360
361 static int proc_signal(struct ubus_context *ctx, struct ubus_object *obj,
362                         struct ubus_request_data *req, const char *method,
363                         struct blob_attr *msg)
364 {
365         struct blob_attr *tb[__SIGNAL_MAX];
366
367         if (!msg)
368                 return UBUS_STATUS_INVALID_ARGUMENT;
369
370         blobmsg_parse(signal_policy, __SIGNAL_MAX, tb, blob_data(msg), blob_len(msg));
371         if (!tb[SIGNAL_PID || !tb[SIGNAL_NUM]])
372                 return UBUS_STATUS_INVALID_ARGUMENT;
373
374         kill(blobmsg_get_u32(tb[SIGNAL_PID]), blobmsg_get_u32(tb[SIGNAL_NUM]));
375
376         return 0;
377 }
378
379 enum {
380         SYSUPGRADE_PATH,
381         SYSUPGRADE_PREFIX,
382         SYSUPGRADE_COMMAND,
383         __SYSUPGRADE_MAX
384 };
385
386 static const struct blobmsg_policy sysupgrade_policy[__SYSUPGRADE_MAX] = {
387         [SYSUPGRADE_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
388         [SYSUPGRADE_PREFIX] = { .name = "prefix", .type = BLOBMSG_TYPE_STRING },
389         [SYSUPGRADE_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
390 };
391
392 static int sysupgrade(struct ubus_context *ctx, struct ubus_object *obj,
393                       struct ubus_request_data *req, const char *method,
394                       struct blob_attr *msg)
395 {
396         struct blob_attr *tb[__SYSUPGRADE_MAX];
397
398         if (!msg)
399                 return UBUS_STATUS_INVALID_ARGUMENT;
400
401         blobmsg_parse(sysupgrade_policy, __SYSUPGRADE_MAX, tb, blob_data(msg), blob_len(msg));
402         if (!tb[SYSUPGRADE_PATH] || !tb[SYSUPGRADE_PREFIX])
403                 return UBUS_STATUS_INVALID_ARGUMENT;
404
405         sysupgrade_exec_upgraded(blobmsg_get_string(tb[SYSUPGRADE_PREFIX]),
406                                  blobmsg_get_string(tb[SYSUPGRADE_PATH]),
407                                  tb[SYSUPGRADE_COMMAND] ? blobmsg_get_string(tb[SYSUPGRADE_COMMAND]) : NULL);
408
409         /* sysupgrade_exec_upgraded() will never return unless something has gone wrong */
410         return UBUS_STATUS_UNKNOWN_ERROR;
411 }
412
413 static void
414 procd_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
415 {
416         notify = obj->has_subscribers;
417 }
418
419
420 static const struct ubus_method system_methods[] = {
421         UBUS_METHOD_NOARG("board", system_board),
422         UBUS_METHOD_NOARG("info",  system_info),
423         UBUS_METHOD_NOARG("reboot", system_reboot),
424         UBUS_METHOD("watchdog", watchdog_set, watchdog_policy),
425         UBUS_METHOD("signal", proc_signal, signal_policy),
426         UBUS_METHOD("sysupgrade", sysupgrade, sysupgrade_policy),
427 };
428
429 static struct ubus_object_type system_object_type =
430         UBUS_OBJECT_TYPE("system", system_methods);
431
432 static struct ubus_object system_object = {
433         .name = "system",
434         .type = &system_object_type,
435         .methods = system_methods,
436         .n_methods = ARRAY_SIZE(system_methods),
437         .subscribe_cb = procd_subscribe_cb,
438 };
439
440 void
441 procd_bcast_event(char *event, struct blob_attr *msg)
442 {
443         int ret;
444
445         if (!notify)
446                 return;
447
448         ret = ubus_notify(_ctx, &system_object, event, msg, -1);
449         if (ret)
450                 fprintf(stderr, "Failed to notify log: %s\n", ubus_strerror(ret));
451 }
452
453 void ubus_init_system(struct ubus_context *ctx)
454 {
455         int ret;
456
457         _ctx = ctx;
458         ret = ubus_add_object(ctx, &system_object);
459         if (ret)
460                 ERROR("Failed to add object: %s\n", ubus_strerror(ret));
461 }