trace: add missing limits.h include
[project/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 "watchdog.h"
32
33 static struct blob_buf b;
34 static int notify;
35 static struct ubus_context *_ctx;
36
37 int upgrade_running = 0;
38
39 static int system_board(struct ubus_context *ctx, struct ubus_object *obj,
40                  struct ubus_request_data *req, const char *method,
41                  struct blob_attr *msg)
42 {
43         void *c;
44         char line[256];
45         char *key, *val, *next;
46         struct utsname utsname;
47         FILE *f;
48
49         blob_buf_init(&b, 0);
50
51         if (uname(&utsname) >= 0)
52         {
53                 blobmsg_add_string(&b, "kernel", utsname.release);
54                 blobmsg_add_string(&b, "hostname", utsname.nodename);
55         }
56
57         if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
58         {
59                 while(fgets(line, sizeof(line), f))
60                 {
61                         key = strtok(line, "\t:");
62                         val = strtok(NULL, "\t\n");
63
64                         if (!key || !val)
65                                 continue;
66
67                         if (!strcasecmp(key, "system type") ||
68                             !strcasecmp(key, "processor") ||
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_upgrade(struct ubus_context *ctx, struct ubus_object *obj,
273                         struct ubus_request_data *req, const char *method,
274                         struct blob_attr *msg)
275 {
276         upgrade_running = 1;
277         return 0;
278 }
279
280 static int system_reboot(struct ubus_context *ctx, struct ubus_object *obj,
281                          struct ubus_request_data *req, const char *method,
282                          struct blob_attr *msg)
283 {
284         procd_shutdown(RB_AUTOBOOT);
285         return 0;
286 }
287
288 enum {
289         WDT_FREQUENCY,
290         WDT_TIMEOUT,
291         WDT_MAGICCLOSE,
292         WDT_STOP,
293         __WDT_MAX
294 };
295
296 static const struct blobmsg_policy watchdog_policy[__WDT_MAX] = {
297         [WDT_FREQUENCY] = { .name = "frequency", .type = BLOBMSG_TYPE_INT32 },
298         [WDT_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 },
299         [WDT_MAGICCLOSE] = { .name = "magicclose", .type = BLOBMSG_TYPE_BOOL },
300         [WDT_STOP] = { .name = "stop", .type = BLOBMSG_TYPE_BOOL },
301 };
302
303 static int watchdog_set(struct ubus_context *ctx, struct ubus_object *obj,
304                         struct ubus_request_data *req, const char *method,
305                         struct blob_attr *msg)
306 {
307         struct blob_attr *tb[__WDT_MAX];
308         const char *status;
309
310         if (!msg)
311                 return UBUS_STATUS_INVALID_ARGUMENT;
312
313         blobmsg_parse(watchdog_policy, __WDT_MAX, tb, blob_data(msg), blob_len(msg));
314         if (tb[WDT_FREQUENCY]) {
315                 unsigned int timeout = watchdog_timeout(0);
316                 unsigned int freq = blobmsg_get_u32(tb[WDT_FREQUENCY]);
317
318                 if (freq) {
319                         if (freq > timeout / 2)
320                                 freq = timeout / 2;
321                         watchdog_frequency(freq);
322                 }
323         }
324
325         if (tb[WDT_TIMEOUT]) {
326                 unsigned int timeout = blobmsg_get_u32(tb[WDT_TIMEOUT]);
327                 unsigned int frequency = watchdog_frequency(0);
328
329                 if (timeout <= frequency)
330                         timeout = frequency * 2;
331                  watchdog_timeout(timeout);
332         }
333
334         if (tb[WDT_MAGICCLOSE])
335                 watchdog_set_magicclose(blobmsg_get_bool(tb[WDT_MAGICCLOSE]));
336
337         if (tb[WDT_STOP])
338                 watchdog_set_stopped(blobmsg_get_bool(tb[WDT_STOP]));
339
340         if (watchdog_fd() == NULL)
341                 status = "offline";
342         else if (watchdog_get_stopped())
343                 status = "stopped";
344         else
345                 status = "running";
346
347         blob_buf_init(&b, 0);
348         blobmsg_add_string(&b, "status", status);
349         blobmsg_add_u32(&b, "timeout", watchdog_timeout(0));
350         blobmsg_add_u32(&b, "frequency", watchdog_frequency(0));
351         blobmsg_add_u8(&b, "magicclose", watchdog_get_magicclose());
352         ubus_send_reply(ctx, req, b.head);
353
354         return 0;
355 }
356
357 enum {
358         SIGNAL_PID,
359         SIGNAL_NUM,
360         __SIGNAL_MAX
361 };
362
363 static const struct blobmsg_policy signal_policy[__SIGNAL_MAX] = {
364         [SIGNAL_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
365         [SIGNAL_NUM] = { .name = "signum", .type = BLOBMSG_TYPE_INT32 },
366 };
367
368 static int proc_signal(struct ubus_context *ctx, struct ubus_object *obj,
369                         struct ubus_request_data *req, const char *method,
370                         struct blob_attr *msg)
371 {
372         struct blob_attr *tb[__SIGNAL_MAX];
373
374         if (!msg)
375                 return UBUS_STATUS_INVALID_ARGUMENT;
376
377         blobmsg_parse(signal_policy, __SIGNAL_MAX, tb, blob_data(msg), blob_len(msg));
378         if (!tb[SIGNAL_PID || !tb[SIGNAL_NUM]])
379                 return UBUS_STATUS_INVALID_ARGUMENT;
380
381         kill(blobmsg_get_u32(tb[SIGNAL_PID]), blobmsg_get_u32(tb[SIGNAL_NUM]));
382
383         return 0;
384 }
385
386 enum {
387         NAND_PATH,
388         __NAND_MAX
389 };
390
391 static const struct blobmsg_policy nand_policy[__NAND_MAX] = {
392         [NAND_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
393 };
394
395 static void
396 procd_spawn_upgraded(char *path)
397 {
398         char *wdt_fd = watchdog_fd();
399         char *argv[] = { "/tmp/upgraded", NULL, NULL};
400
401         argv[1] = path;
402
403         DEBUG(2, "Exec to upgraded now\n");
404         if (wdt_fd) {
405                 watchdog_no_cloexec();
406                 setenv("WDTFD", wdt_fd, 1);
407         }
408         execvp(argv[0], argv);
409 }
410
411 static int nand_set(struct ubus_context *ctx, struct ubus_object *obj,
412                         struct ubus_request_data *req, const char *method,
413                         struct blob_attr *msg)
414 {
415         struct blob_attr *tb[__NAND_MAX];
416
417         if (!msg)
418                 return UBUS_STATUS_INVALID_ARGUMENT;
419
420         blobmsg_parse(nand_policy, __NAND_MAX, tb, blob_data(msg), blob_len(msg));
421         if (!tb[NAND_PATH])
422                 return UBUS_STATUS_INVALID_ARGUMENT;
423
424         procd_spawn_upgraded(blobmsg_get_string(tb[NAND_PATH]));
425         fprintf(stderr, "Yikees, something went wrong. no /sbin/upgraded ?\n");
426         return 0;
427 }
428
429 static void
430 procd_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
431 {
432         notify = obj->has_subscribers;
433 }
434
435
436 static const struct ubus_method system_methods[] = {
437         UBUS_METHOD_NOARG("board", system_board),
438         UBUS_METHOD_NOARG("info",  system_info),
439         UBUS_METHOD_NOARG("upgrade", system_upgrade),
440         UBUS_METHOD_NOARG("reboot", system_reboot),
441         UBUS_METHOD("watchdog", watchdog_set, watchdog_policy),
442         UBUS_METHOD("signal", proc_signal, signal_policy),
443
444         /* must remain at the end as it ia not always loaded */
445         UBUS_METHOD("nandupgrade", nand_set, nand_policy),
446 };
447
448 static struct ubus_object_type system_object_type =
449         UBUS_OBJECT_TYPE("system", system_methods);
450
451 static struct ubus_object system_object = {
452         .name = "system",
453         .type = &system_object_type,
454         .methods = system_methods,
455         .n_methods = ARRAY_SIZE(system_methods),
456         .subscribe_cb = procd_subscribe_cb,
457 };
458
459 void
460 procd_bcast_event(char *event, struct blob_attr *msg)
461 {
462         int ret;
463
464         if (!notify)
465                 return;
466
467         ret = ubus_notify(_ctx, &system_object, event, msg, -1);
468         if (ret)
469                 fprintf(stderr, "Failed to notify log: %s\n", ubus_strerror(ret));
470 }
471
472 void ubus_init_system(struct ubus_context *ctx)
473 {
474         struct stat s;
475         int ret;
476
477         if (stat("/sbin/upgraded", &s))
478                 system_object.n_methods -= 1;
479
480         _ctx = ctx;
481         ret = ubus_add_object(ctx, &system_object);
482         if (ret)
483                 ERROR("Failed to add object: %s\n", ubus_strerror(ret));
484 }