blob: 81ed276522451a8c85ad73a346c2ff753347ae49 [file] [log] [blame]
Pau Espin Pedrol92016772019-03-18 17:24:34 +01001/* Simple Osmocom System Monitor (osysmon): Support for monitoring through shell commands */
2
3/* (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
4 * All Rights Reserved.
5 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
6 *
7 * SPDX-License-Identifier: GPL-2.0+
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
Pau Espin Pedrol92016772019-03-18 17:24:34 +010018 */
19
20#include <string.h>
21#include <stdio.h>
22#include <errno.h>
23#include <sys/sysinfo.h>
24
25#include <osmocom/vty/vty.h>
26#include <osmocom/vty/command.h>
27
28#include "osysmon.h"
29#include "value_node.h"
30
31/***********************************************************************
32 * Data model
33 ***********************************************************************/
34
35struct osysmon_shellcmd {
36 struct llist_head list;
37 struct {
38 const char *name;
39 const char *cmd;
40 } cfg;
41};
42
43static struct osysmon_shellcmd *osysmon_shellcmd_find(const char *name)
44{
45 struct osysmon_shellcmd *oc;
46
47 llist_for_each_entry(oc, &g_oss->shellcmds, list) {
48 if (!strcmp(oc->cfg.name, name))
49 return oc;
50 }
51 return NULL;
52}
53
54static struct osysmon_shellcmd *osysmon_shellcmd_add(const char *name, const char *cmd)
55{
56 struct osysmon_shellcmd *oc;
57
58 if (osysmon_shellcmd_find(name))
59 return NULL;
60
61 oc = talloc_zero(g_oss, struct osysmon_shellcmd);
62 OSMO_ASSERT(oc);
63 oc->cfg.name = talloc_strdup(oc, name);
64 oc->cfg.cmd = talloc_strdup(oc, cmd);
65 llist_add_tail(&oc->list, &g_oss->shellcmds);
66 return oc;
67}
68
69static void osysmon_shellcmd_destroy(struct osysmon_shellcmd *oc)
70{
71 llist_del(&oc->list);
72 talloc_free(oc);
73}
74
75static void osysmon_shellcmd_run(struct osysmon_shellcmd *oc, struct value_node *parent)
76{
77 char buf[512];
78 FILE *f;
79 char *p = buf;
80 long offset = 0;
81
82 f = popen(oc->cfg.cmd, "r");
83 if (!f) {
84 snprintf(buf, sizeof(buf), "<popen failed (%d)>", errno);
85 value_node_add(parent, oc->cfg.name, buf);
86 return;
87 }
88
89 while ((offset = fread(p, 1, sizeof(buf) - 1 - (p - buf), f)) != 0) {
90 p += offset;
91 *p = '\0';
92 }
93
94 pclose(f);
95
96 if (buf != p) {
97 if (*(p - 1) == '\n') /* Remove final new line if exists */
98 *(p - 1) = '\0';
99 value_node_add(parent, oc->cfg.name, buf);
100 } else {
101 value_node_add(parent, oc->cfg.name, "<EMPTY>");
102 }
103}
104
105/***********************************************************************
106 * VTY
107 ***********************************************************************/
108
109#define CMD_STR "Configure a shell command to be executed\n"
110DEFUN(cfg_shellcmd, cfg_shellcmd_cmd,
111 "shellcmd NAME .TEXT",
112 CMD_STR "Name of this shell command snippet\n" "Command to run\n")
113{
114 struct osysmon_shellcmd *oc;
115 char *concat = argv_concat(argv, argc, 1);
116 oc = osysmon_shellcmd_add(argv[0], concat);
117 talloc_free(concat);
118 if (!oc) {
119 vty_out(vty, "Couldn't add shell cmd, maybe it exists?%s", VTY_NEWLINE);
120 return CMD_WARNING;
121 }
122 return CMD_SUCCESS;
123}
124
125DEFUN(cfg_no_shellcmd, cfg_no_shellcmd_cmd,
126 "no shellcmd NAME",
127 NO_STR CMD_STR "Name of this shell command snippet\n")
128{
129 struct osysmon_shellcmd *oc;
130 oc = osysmon_shellcmd_find(argv[0]);
131 if (!oc) {
132 vty_out(vty, "Cannot find shell cmd for '%s'%s", argv[0], VTY_NEWLINE);
133 return CMD_WARNING;
134 }
135 osysmon_shellcmd_destroy(oc);
136 return CMD_SUCCESS;
137}
138
139
140static void osysmon_shellcmd_vty_init(void)
141{
142 install_element(CONFIG_NODE, &cfg_shellcmd_cmd);
143 install_element(CONFIG_NODE, &cfg_no_shellcmd_cmd);
144}
145
146/***********************************************************************
147 * Runtime Code
148 ***********************************************************************/
149
150/* called once on startup before config file parsing */
151int osysmon_shellcmd_init()
152{
153 osysmon_shellcmd_vty_init();
154 return 0;
155}
156
157/* called periodically */
158int osysmon_shellcmd_poll(struct value_node *parent)
159{
160 struct value_node *vn_file;
161 struct osysmon_shellcmd *oc;
162
Daniel Willmann85d0fb22019-11-07 16:59:15 +0100163 if (llist_empty(&g_oss->shellcmds))
164 return 0;
165
Pau Espin Pedrol92016772019-03-18 17:24:34 +0100166 vn_file = value_node_add(parent, "shellcmd", NULL);
167
168 llist_for_each_entry(oc, &g_oss->shellcmds, list)
169 osysmon_shellcmd_run(oc, vn_file);
170
171 return 0;
172}