2 * Copyright (C) 2016 "IoT.bzh"
3 * Author José Bollo <jose.bollo@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
27 #include <sys/types.h>
28 #include <sys/timerfd.h>
30 #include <json-c/json.h>
32 #include <systemd/sd-event.h>
34 #include <afb/afb-binding.h>
35 #include <afb/afb-service-itf.h>
38 * the interface to afb-daemon
40 const struct afb_binding_interface *afbitf;
42 #define DEFAULT_PERIOD 1 /* 1 second */
46 const char *cpu_fields[]= {
59 #define CPU_FIELD_COUNT (sizeof cpu_fields / sizeof *cpu_fields)
62 uint64_t cpu[CPU_FIELD_COUNT];
67 static struct sd_event_source *source = NULL;
69 static struct status older;
70 static struct status newer;
71 static struct status diff;
72 static struct afb_event event;
74 /***************************************************************************************/
75 /***************************************************************************************/
78 /** SECTION: BINDING VERBS IMPLEMENTATION **/
81 /***************************************************************************************/
82 /***************************************************************************************/
84 static int read_status(int fd, struct status *s)
90 long long unsigned x[CPU_FIELD_COUNT];
92 off = lseek(fd, 0, SEEK_SET);
96 sz = read(fd, buffer, sizeof buffer);
100 n = sscanf(buffer, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], &x[8], &x[9]);
101 if (n != CPU_FIELD_COUNT)
112 sd_event_source_unref(source);
113 afb_event_drop(event);
122 static int emit(sd_event_source *src, int fd, uint32_t revents, void *userdata)
125 struct json_object *obj;
128 read(fd, &i, sizeof i);
130 memcpy(&older, &newer, sizeof older);
131 rc = read_status(fd_proc, &newer);
132 for(f = 0; f < (int)CPU_FIELD_COUNT ; f++)
133 diff.cpu[f] = newer.cpu[f] - older.cpu[f];
138 u += diff.cpu[0]; // "user",
139 u += diff.cpu[1]; // "nice",
140 s += diff.cpu[2]; // "system",
141 i += diff.cpu[3]; // "idle",
142 i += diff.cpu[4]; // "iowait",
143 s += diff.cpu[5]; // "irq",
144 s += diff.cpu[6]; // "softirq",
145 i += diff.cpu[7]; // "steal",
146 u += diff.cpu[8]; // "guest",
147 u += diff.cpu[9]; // "guest_nice"
148 p = (int)(unsigned)((100 * (u + s)) / (u + s + i));
150 obj = json_object_new_int(p);
151 if (afb_event_push(event, obj) == 0)
159 struct itimerspec ts;
160 struct sd_event_source *src;
162 fdp = open("/proc/stat", O_RDONLY|O_CLOEXEC);
164 rc = read_status(fdp, &newer);
166 fdt = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
168 ts.it_interval.tv_sec = 1;
169 ts.it_value.tv_sec = 1;
170 ts.it_interval.tv_nsec = 0;
171 ts.it_value.tv_nsec = 0;
172 rc = timerfd_settime(fdt, 0, &ts, NULL);
174 event = afb_daemon_make_event(afbitf->daemon, "stat");
175 if (afb_event_is_valid(event)) {
176 rc = sd_event_add_io(afb_daemon_get_event_loop(afbitf->daemon), &src, fdt, EPOLLIN, emit, NULL);
183 afb_event_drop(event);
194 static int ensure_started()
196 return source == NULL ? start() : 0;
199 /***************************************************************************************/
200 /***************************************************************************************/
203 /** SECTION: BINDING VERBS IMPLEMENTATION **/
206 /***************************************************************************************/
207 /***************************************************************************************/
210 * subscribe to notification of stat
212 static void subscribe(struct afb_req req)
216 rc = ensure_started();
218 afb_req_fail(req, "failed", "Can't start");
219 } else if (afb_req_subscribe(req, event) != 0) {
220 afb_req_fail_f(req, "failed", "afb_req_subscribe returned an error: %m");
222 afb_req_success(req, NULL, NULL);
227 * unsubscribe a previous subscription
229 * parameters of the unsubscription are:
231 * id: integer: the numeric identifier of the event as returned when subscribing
233 static void unsubscribe(struct afb_req req)
235 if (afb_event_is_valid(event))
236 afb_req_unsubscribe(req, event);
237 afb_req_success(req, NULL, NULL);
241 * array of the verbs exported to afb-daemon
243 static const struct afb_verb_desc_v1 binding_verbs[] = {
244 /* VERB'S NAME SESSION MANAGEMENT FUNCTION TO CALL SHORT DESCRIPTION */
245 { .name= "subscribe", .session= AFB_SESSION_NONE, .callback= subscribe, .info= "subscribe to notification of statistics" },
246 { .name= "unsubscribe", .session= AFB_SESSION_NONE, .callback= unsubscribe, .info= "unsubscribe a previous subscription" },
247 { .name= NULL } /* marker for end of the array */
251 * description of the binding for afb-daemon
253 static const struct afb_binding binding_description =
255 /* description conforms to VERSION 1 */
256 .type= AFB_BINDING_VERSION_1,
257 .v1= { /* fills the v1 field of the union when AFB_BINDING_VERSION_1 */
258 .prefix= "stat", /* the API name (or binding name or prefix) */
259 .info= "Get system statistics", /* short description of of the binding */
260 .verbs = binding_verbs /* the array describing the verbs of the API */
265 * activation function for registering the binding called by afb-daemon
267 const struct afb_binding *afbBindingV1Register(const struct afb_binding_interface *itf)
269 afbitf = itf; /* records the interface for accessing afb-daemon */
270 return &binding_description; /* returns the description of the binding */