Let the thread to be managed outside the binding.
[apps/agl-service-can-low-level.git] / CAN-binder / low-can-demo / binding / stat-binding.c
1 /*
2  * Copyright (C) 2016 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
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
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 #define _GNU_SOURCE
19 #include <stdio.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <errno.h>
23 #include <netdb.h>
24 #include <fcntl.h>
25 #include <math.h>
26 #include <sys/time.h>
27 #include <sys/types.h>
28 #include <sys/timerfd.h>
29
30 #include <json-c/json.h>
31
32 #include <systemd/sd-event.h>
33
34 #include <afb/afb-binding.h>
35 #include <afb/afb-service-itf.h>
36
37 /*
38  * the interface to afb-daemon
39  */
40 const struct afb_binding_interface *afbitf;
41
42 #define DEFAULT_PERIOD   1   /* 1 second */
43
44 struct event;
45
46 const char *cpu_fields[]= {
47         "user",
48         "nice",
49         "system",
50         "idle",
51         "iowait",
52         "irq",
53         "softirq",
54         "steal",
55         "guest",
56         "guest_nice"
57 };
58
59 #define CPU_FIELD_COUNT (sizeof cpu_fields / sizeof *cpu_fields)
60
61 struct status {
62         uint64_t cpu[CPU_FIELD_COUNT];
63 };
64
65 static int fd_proc;
66 static int fd_timer;
67 static struct sd_event_source *source = NULL;
68
69 static struct status older;
70 static struct status newer;
71 static struct status diff;
72 static struct afb_event event;
73
74 /***************************************************************************************/
75 /***************************************************************************************/
76 /**                                                                                   **/
77 /**                                                                                   **/
78 /**       SECTION: BINDING VERBS IMPLEMENTATION                                       **/
79 /**                                                                                   **/
80 /**                                                                                   **/
81 /***************************************************************************************/
82 /***************************************************************************************/
83
84 static int read_status(int fd, struct status *s)
85 {
86         int n;
87         ssize_t sz;
88         off_t off;
89         char buffer[8192];
90         long long unsigned x[CPU_FIELD_COUNT];
91
92         off = lseek(fd, 0, SEEK_SET);
93         if (off == (off_t)-1)
94                 return -1;
95
96         sz = read(fd, buffer, sizeof buffer);
97         if (sz == -1)
98                 return -1;
99
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)
102                 return -1;
103         while(n) {
104                 n--;
105                 s->cpu[n] = x[n];
106         }
107         return 0;
108 }
109
110 static void stop()
111 {
112         sd_event_source_unref(source);
113         afb_event_drop(event);
114         close(fd_timer);
115         close(fd_proc);
116         fd_timer = -1;
117         fd_proc = -1;
118         event.itf = NULL;
119         source = NULL;
120 }
121
122 static int emit(sd_event_source *src, int fd, uint32_t revents, void *userdata)
123 {
124         int f, rc, p;
125         struct json_object *obj;
126         uint64_t u, s, i;
127
128         read(fd, &i, sizeof i);
129
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];
134         
135         u = 0;
136         s = 0;
137         i = 0;
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));
149
150         obj = json_object_new_int(p);
151         if (afb_event_push(event, obj) == 0)
152                 stop();
153         return 0;
154 }
155
156 static int start()
157 {
158         int fdp, fdt, rc;
159         struct itimerspec ts;
160         struct sd_event_source *src;
161
162         fdp = open("/proc/stat", O_RDONLY|O_CLOEXEC);
163         if (fdp >= 0) {
164                 rc = read_status(fdp, &newer);
165                 if (rc >= 0) {
166                         fdt = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
167                         if (fdt >= 0) {
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);
173                                 if (rc >= 0) {
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);
177                                                 if (rc >= 0) {
178                                                         fd_proc = fdp;
179                                                         fd_timer = fdt;
180                                                         source = src;
181                                                         return 0;
182                                                 }
183                                                 afb_event_drop(event);
184                                         }
185                                 }
186                                 close(fdt);
187                         }
188                 }
189                 close(fdp);
190         }
191         return -1;
192 }
193
194 static int ensure_started()
195 {
196         return source == NULL ? start() : 0;
197 }
198
199 /***************************************************************************************/
200 /***************************************************************************************/
201 /**                                                                                   **/
202 /**                                                                                   **/
203 /**       SECTION: BINDING VERBS IMPLEMENTATION                                       **/
204 /**                                                                                   **/
205 /**                                                                                   **/
206 /***************************************************************************************/
207 /***************************************************************************************/
208
209 /*
210  * subscribe to notification of stat
211  */
212 static void subscribe(struct afb_req req)
213 {
214         int rc;
215
216         rc = ensure_started();
217         if (rc < 0) {
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");
221         } else {
222                 afb_req_success(req, NULL, NULL);
223         }
224 }
225
226 /*
227  * unsubscribe a previous subscription
228  *
229  * parameters of the unsubscription are:
230  *
231  *    id:   integer: the numeric identifier of the event as returned when subscribing
232  */
233 static void unsubscribe(struct afb_req req)
234 {
235         if (afb_event_is_valid(event))
236                 afb_req_unsubscribe(req, event);
237         afb_req_success(req, NULL, NULL);
238 }
239
240 /*
241  * array of the verbs exported to afb-daemon
242  */
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 */
248 };
249
250 /*
251  * description of the binding for afb-daemon
252  */
253 static const struct afb_binding binding_description =
254 {
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 */
261   }
262 };
263
264 /*
265  * activation function for registering the binding called by afb-daemon
266  */
267 const struct afb_binding *afbBindingV1Register(const struct afb_binding_interface *itf)
268 {
269         afbitf = itf;                   /* records the interface for accessing afb-daemon */
270         return &binding_description;    /* returns the description of the binding */
271 }
272