Update version of tools
[src/app-framework-main.git] / src / afm-system-daemon.c
1 /*
2  Copyright 2015, 2016, 2017 IoT.bzh
3
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 #include <unistd.h>
20 #include <stdio.h>
21 #include <time.h>
22 #include <getopt.h>
23 #include <errno.h>
24
25 #include <systemd/sd-bus.h>
26 #include <systemd/sd-event.h>
27 #include <json-c/json.h>
28
29 #include "verbose.h"
30 #include "utils-jbus.h"
31 #include "utils-json.h"
32 #include "utils-systemd.h"
33 #include "afm.h"
34 #include "afm-db.h"
35 #include "wgt-info.h"
36 #include "wgtpkg-install.h"
37 #include "wgtpkg-uninstall.h"
38
39 static const char appname[] = "afm-system-daemon";
40 static const char *rootdir = NULL;
41
42 static void version()
43 {
44         printf(
45                 "\n"
46                 "  %s  version="AFM_VERSION"\n"
47                 "\n"
48                 "  Copyright (C) 2015, 2016, 2017 \"IoT.bzh\"\n"
49                 "  AFB comes with ABSOLUTELY NO WARRANTY.\n"
50                 "  Licence Apache 2\n"
51                 "\n",
52                 appname
53         );
54 }
55
56 static void usage()
57 {
58         printf(
59                 "usage: %s [-q] [-v] [-r rootdir]\n"
60                 "\n"
61                 "   -r rootdir   set root directory of applications\n"
62                 "   -d           run as a daemon\n"
63                 "   -q           quiet\n"
64                 "   -v           verbose\n"
65                 "   -V           version\n"
66                 "\n",
67                 appname
68         );
69 }
70
71 static struct option options[] = {
72         { "root",        required_argument, NULL, 'r' },
73         { "daemon",      no_argument,       NULL, 'd' },
74         { "quiet",       no_argument,       NULL, 'q' },
75         { "verbose",     no_argument,       NULL, 'v' },
76         { "help",        no_argument,       NULL, 'h' },
77         { "version",     no_argument,       NULL, 'V' },
78         { NULL, 0, NULL, 0 }
79 };
80
81 static struct jbus *jbus;
82
83 const char error_nothing[] = "[]";
84 const char error_bad_request[] = "\"bad request\"";
85 const char error_not_found[] = "\"not found\"";
86 const char error_cant_start[] = "\"can't start\"";
87
88 static void do_reloads()
89 {
90 #ifndef LEGACY_MODE_WITHOUT_SYSTEMD
91         /* enforce daemon reload */
92         systemd_daemon_reload(0);
93         systemd_unit_restart_name(0, "sockets.target");
94 #endif
95 }
96
97 static void on_install(struct sd_bus_message *smsg, struct json_object *req, void *unused)
98 {
99         const char *wgtfile;
100         const char *root;
101         int force;
102         int reload;
103         struct wgt_info *ifo;
104         struct json_object *resp;
105
106         /* scan the request */
107         switch (json_object_get_type(req)) {
108         case json_type_string:
109                 wgtfile = json_object_get_string(req);
110                 root = rootdir;
111                 force = 0;
112                 reload = 1;
113                 break;
114         case json_type_object:
115                 wgtfile = j_string_at(req, "wgt", NULL);
116                 if (wgtfile != NULL) {
117                         root = j_string_at(req, "root", rootdir);
118                         force = j_boolean_at(req, "force", 0);
119                         reload = j_boolean_at(req, "reload", 1);
120                         break;
121                 }
122                 /*@fallthrough@*/
123         default:
124                 jbus_reply_error_s(smsg, error_bad_request);
125                 return;
126         }
127
128         /* install the widget */
129         ifo = install_widget(wgtfile, root, force);
130         if (ifo == NULL)
131                 jbus_reply_error_s(smsg, "\"installation failed\"");
132         else {
133                 /* reload if needed */
134                 if (reload)
135                         do_reloads();
136
137                 /* build the response */
138                 resp = json_object_new_object();
139                 if(!resp || !j_add_string(resp, "added", wgt_info_desc(ifo)->idaver))
140                         jbus_reply_error_s(smsg, "\"out of memory but installed!\"");
141                 else {
142                         jbus_send_signal_s(jbus, "changed", "true");
143                         jbus_reply_j(smsg, resp);
144                 }
145
146                 /* clean-up */
147                 wgt_info_unref(ifo);
148                 json_object_put(resp);
149         }
150 }
151
152 static void on_uninstall(struct sd_bus_message *smsg, struct json_object *req, void *unused)
153 {
154         const char *idaver;
155         const char *root;
156         int rc;
157
158         /* scan the request */
159         switch (json_object_get_type(req)) {
160         case json_type_string:
161                 idaver = json_object_get_string(req);
162                 root = rootdir;
163                 break;
164         case json_type_object:
165                 idaver = j_string_at(req, "id", NULL);
166                 if (idaver != NULL) {
167                         root = j_string_at(req, "root", rootdir);
168                         break;
169                 }
170                 /*@fallthrough@*/
171         default:
172                 jbus_reply_error_s(smsg, error_bad_request);
173                 return;
174         }
175
176         /* install the widget */
177         rc = uninstall_widget(idaver, root);
178         if (rc)
179                 jbus_reply_error_s(smsg, "\"uninstallation had error\"");
180         else {
181                 jbus_send_signal_s(jbus, "changed", "true");
182                 jbus_reply_s(smsg, "true");
183         }
184 }
185
186 static int daemonize()
187 {
188         int rc = fork();
189         if (rc < 0)
190                 return rc;
191         if (rc)
192                 _exit(0);
193         return 0;
194 }
195
196 int main(int ac, char **av)
197 {
198         int i, daemon = 0, rc;
199         struct sd_event *evloop;
200         struct sd_bus *sysbus;
201
202         LOGAUTH(appname);
203
204         /* interpretation of arguments */
205         while ((i = getopt_long(ac, av, "hdqvVr:", options, NULL)) >= 0) {
206                 switch (i) {
207                 case 'h':
208                         usage();
209                         return 0;
210                 case 'V':
211                         version();
212                         return 0;
213                 case 'q':
214                         if (verbosity)
215                                 verbosity--;
216                         break;
217                 case 'v':
218                         verbosity++;
219                         break;
220                 case 'd':
221                         daemon = 1;
222                         break;
223                 case 'r':
224                         if (rootdir == NULL)
225                                 rootdir = optarg;
226                         else {
227                                 ERROR("duplicate definition of rootdir");
228                                 return 1;
229                         }
230                         break;
231                 case ':':
232                         ERROR("missing argument value");
233                         return 1;
234                 default:
235                         ERROR("unrecognized option");
236                         return 1;
237                 }
238         }
239
240         /* check the rootdir */
241         if (rootdir == NULL)
242                 rootdir = FWK_APP_DIR;
243         else {
244                 rootdir = realpath(rootdir, NULL);
245                 if (rootdir == NULL) {
246                         ERROR("out of memory");
247                         return 1;
248                 }
249         }
250         if (chdir(rootdir)) {
251                 ERROR("can't enter %s", rootdir);
252                 return 1;
253         }
254
255         /* daemonize */
256         if (daemon && daemonize()) {
257                 ERROR("daemonization failed");
258                 return 1;
259         }
260
261         /* get systemd objects */
262         rc = sd_event_new(&evloop);
263         if (rc < 0) {
264                 ERROR("can't create event loop");
265                 return 1;
266         }
267         rc = sd_bus_open_system(&sysbus);
268         if (rc < 0) {
269                 ERROR("can't create system bus");
270                 return 1;
271         }
272         rc = sd_bus_attach_event(sysbus, evloop, 0);
273         if (rc < 0) {
274                 ERROR("can't attach system bus to event loop");
275                 return 1;
276         }
277
278         /* init service */
279         jbus = create_jbus(sysbus, AFM_SYSTEM_DBUS_PATH);
280         if (!jbus) {
281                 ERROR("create_jbus failed");
282                 return 1;
283         }
284         if(jbus_add_service_j(jbus, "install", on_install, NULL)
285         || jbus_add_service_j(jbus, "uninstall", on_uninstall, NULL)) {
286                 ERROR("adding services failed");
287                 return 1;
288         }
289
290         /* start and run */
291         if (jbus_start_serving(jbus) < 0) {
292                 ERROR("can't start server");
293                 return 1;
294         }
295         for(;;)
296                 sd_event_run(evloop, (uint64_t)-1);
297         return 0;
298 }