305e1b341cf8e4dbb7fca95e3b772df835ed441f
[src/app-framework-binder.git] / src / afb-client-demo.c
1 /*
2  * Copyright (C) 2015, 2016, 2017 "IoT.bzh"
3  * Author "Fulup Ar Foll"
4  * Author José Bollo <jose.bollo@iot.bzh>
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #define _GNU_SOURCE
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stdint.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <errno.h>
30
31 #include <systemd/sd-event.h>
32 #include <json-c/json.h>
33
34 #include "afb-wsj1.h"
35 #include "afb-ws-client.h"
36
37 /* declaration of functions */
38 static void on_hangup(void *closure, struct afb_wsj1 *wsj1);
39 static void on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg);
40 static void on_event(void *closure, const char *event, struct afb_wsj1_msg *msg);
41 static int io_event_callback(sd_event_source *src, int fd, uint32_t revents, void *closure);
42 static void emit(const char *api, const char *verb, const char *object);
43
44 /* the callback interface for wsj1 */
45 static struct afb_wsj1_itf itf = {
46         .on_hangup = on_hangup,
47         .on_call = on_call,
48         .on_event = on_event
49 };
50
51 /* global variables */
52 static struct afb_wsj1 *wsj1;
53 static int exonrep;
54 static int callcount;
55 static int human;
56 static int raw;
57 static sd_event_source *evsrc;
58
59 /* print usage of the program */
60 static void usage(int status, char *arg0)
61 {
62         char *name = strrchr(arg0, '/');
63         name = name ? name + 1 : arg0;
64         fprintf(status ? stderr : stdout, "usage: %s [-H [-r]] uri [api verb [data]]\n", name);
65         exit(status);
66 }
67
68 /* entry function */
69 int main(int ac, char **av, char **env)
70 {
71         int rc;
72         char *a0;
73         sd_event *loop;
74
75         /* get the program name */
76         a0 = av[0];
77
78         /* check options */
79         while (ac > 1 && av[1][0] == '-') {
80                 if (av[1][1] == '-') {
81                         /* long option */
82
83                         if (!strcmp(av[1], "--human")) /* request for human output */
84                                 human = 1;
85
86                         else if (!strcmp(av[1], "--raw")) /* request for raw output */
87                                 raw = 1;
88
89                         /* emit usage and exit */
90                         else
91                                 usage(!!strcmp(av[1], "--help"), a0);
92                 } else {
93                         /* short option(s) */
94                         for (rc = 1 ; av[1][rc] ; rc++)
95                                 switch (av[1][rc]) {
96                                 case 'H': human = 1; break;
97                                 case 'r': raw = 1; break;
98                                 default: usage(av[1][rc] != 'h', a0);
99                                 }
100                 }
101                 av++;
102                 ac--;
103         }
104
105         /* check the argument count */
106         if (ac != 2 && ac != 4 && ac != 5)
107                 usage(1, a0);
108
109         /* set raw by default */
110         if (!human)
111                 raw = 1;
112
113         /* get the default event loop */
114         rc = sd_event_default(&loop);
115         if (rc < 0) {
116                 fprintf(stderr, "connection to default event loop failed: %s\n", strerror(-rc));
117                 return 1;
118         }
119
120         /* connect the websocket wsj1 to the uri given by the first argument */
121         wsj1 = afb_ws_client_connect_wsj1(loop, av[1], &itf, NULL);
122         if (wsj1 == NULL) {
123                 fprintf(stderr, "connection to %s failed: %m\n", av[1]);
124                 return 1;
125         }
126
127         /* test the behaviour */
128         if (ac == 2) {
129                 /* get requests from stdin */
130                 fcntl(0, F_SETFL, O_NONBLOCK);
131                 sd_event_add_io(loop, &evsrc, 0, EPOLLIN, io_event_callback, NULL);
132         } else {
133                 /* the request is defined by the arguments */
134                 exonrep = 1;
135                 emit(av[2], av[3], av[4]);
136         }
137
138         /* loop until end */
139         for(;;)
140                 sd_event_run(loop, 30000000);
141         return 0;
142 }
143
144 /* called when wsj1 hangsup */
145 static void on_hangup(void *closure, struct afb_wsj1 *wsj1)
146 {
147         printf("ON-HANGUP\n");
148         fflush(stdout);
149         exit(0);
150 }
151
152 /* called when wsj1 receives a method invocation */
153 static void on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
154 {
155         int rc;
156         if (raw)
157                 printf("ON-CALL %s/%s(%s)\n", api, verb, afb_wsj1_msg_object_s(msg));
158         if (human)
159                 printf("ON-CALL %s/%s:\n%s\n", api, verb,
160                                 json_object_to_json_string_ext(afb_wsj1_msg_object_j(msg),
161                                                         JSON_C_TO_STRING_PRETTY));
162         fflush(stdout);
163         rc = afb_wsj1_reply_error_s(msg, "\"unimplemented\"", NULL);
164         if (rc < 0)
165                 fprintf(stderr, "replying failed: %m\n");
166 }
167
168 /* called when wsj1 receives an event */
169 static void on_event(void *closure, const char *event, struct afb_wsj1_msg *msg)
170 {
171         if (raw)
172                 printf("ON-EVENT %s(%s)\n", event, afb_wsj1_msg_object_s(msg));
173         if (human)
174                 printf("ON-EVENT %s:\n%s\n", event,
175                                 json_object_to_json_string_ext(afb_wsj1_msg_object_j(msg),
176                                                         JSON_C_TO_STRING_PRETTY));
177         fflush(stdout);
178 }
179
180 /* called when wsj1 receives a reply */
181 static void on_reply(void *closure, struct afb_wsj1_msg *msg)
182 {
183         if (raw)
184                 printf("ON-REPLY %s: %s\n", (char*)closure, afb_wsj1_msg_object_s(msg));
185         if (human)
186                 printf("ON-REPLY %s: %s\n%s\n", (char*)closure,
187                                 afb_wsj1_msg_is_reply_ok(msg) ? "OK" : "ERROR",
188                                 json_object_to_json_string_ext(afb_wsj1_msg_object_j(msg),
189                                                         JSON_C_TO_STRING_PRETTY));
190         fflush(stdout);
191         free(closure);
192         callcount--;
193         if (exonrep && !callcount)
194                 //afb_wsj1_hangup(afb_wsj1_msg_wsj1(msg));
195                 exit(0);
196 }
197
198 /* makes a call */
199 static void call(const char *api, const char *verb, const char *object)
200 {
201         static int num = 0;
202         char *key;
203         int rc;
204
205         /* allocates an id for the request */
206         rc = asprintf(&key, "%d:%s/%s", ++num, api, verb);
207
208         /* send the request */
209         callcount++;
210         rc = afb_wsj1_call_s(wsj1, api, verb, object, on_reply, key);
211         if (rc < 0) {
212                 fprintf(stderr, "calling %s/%s(%s) failed: %m\n", api, verb, object);
213                 callcount--;
214         }
215 }
216
217 /* sends an event */
218 static void event(const char *event, const char *object)
219 {
220         int rc;
221
222         rc = afb_wsj1_send_event_s(wsj1, event, object);
223         if (rc < 0)
224                 fprintf(stderr, "sending !%s(%s) failed: %m\n", event, object);
225 }
226
227 /* emits either a call (when api!='!') or an event */
228 static void emit(const char *api, const char *verb, const char *object)
229 {
230         if (object == NULL || object[0] == 0)
231                 object = "null";
232         if (api[0] == '!' && api[1] == 0)
233                 event(verb, object);
234         else
235                 call(api, verb, object);
236 }
237
238 /* called when something happens on stdin */
239 static int io_event_callback(sd_event_source *src, int fd, uint32_t revents, void *closure)
240 {
241         static size_t count = 0;
242         static char line[16384];
243         static char sep[] = " \t";
244         static char sepnl[] = " \t\n";
245
246         ssize_t rc;
247         size_t pos;
248
249         /* read the buffer */
250         do { rc = read(0, line + count, sizeof line - count); } while (rc < 0 && errno == EINTR);
251         if (rc < 0) {
252                 fprintf(stderr, "read error: %m\n");
253                 exit(1);
254         }
255         if (rc == 0) {
256                 if (!callcount)
257                         exit(0);
258                 exonrep = 1;
259                 sd_event_source_unref(evsrc);
260         }
261         count += (size_t)rc;
262
263         /* normalise the buffer content */
264         /* TODO: handle backspace \x7f ? */
265
266         /* process the lines */
267         pos = 0;
268         for(;;) {
269                 size_t i, api[2], verb[2], rest[2];
270                 i = pos;
271                 while(i < count && strchr(sep, line[i])) i++;
272                 api[0] = i; while(i < count && !strchr(sepnl, line[i])) i++; api[1] = i;
273                 while(i < count && strchr(sep, line[i])) i++;
274                 verb[0] = i; while(i < count && !strchr(sepnl, line[i])) i++; verb[1] = i;
275                 while(i < count && strchr(sep, line[i])) i++;
276                 rest[0] = i; while(i < count && line[i] != '\n') i++; rest[1] = i;
277                 if (i == count) break;
278                 line[i++] = 0;
279                 if (api[0] == api[1]) {
280                         /* empty line */
281                 } else if (line[api[0]] == '#') {
282                         /* comment */
283                 } else if (verb[0] == verb[1]) {
284                         fprintf(stderr, "verb missing, bad line: %s\n", line+pos);
285                 } else {
286                         line[api[1]] = line[verb[1]] = 0;
287                         emit(line + api[0], line + verb[0], line + rest[0]);
288                 }
289                 pos = i;
290         }
291         count -= pos;
292         if (count == sizeof line) {
293                 fprintf(stderr, "overflow\n");
294                 exit(1);
295         }
296         if (count)
297                 memmove(line, line + pos, count);
298         return 1;
299 }
300