4 author: José Bollo <jose.bollo@iot.bzh>
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
10 http://www.apache.org/licenses/LICENSE-2.0
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.
30 #include <sys/types.h>
32 extern char **environ;
35 #include "afm-launch-mode.h"
36 #include "afm-launch.h"
37 #include "secmgr-wrap.h"
39 #define DEFAULT_TYPE "text/html"
42 struct type_list *next;
52 struct desc_list *next;
53 enum afm_launch_mode mode;
54 struct type_list *types;
55 struct exec_vector execs[2];
64 struct exec_vector *master;
65 struct exec_vector *slave;
77 struct desc_list *launchers = NULL;
79 static gid_t groupid = 0;
81 const char separators[] = " \t\n";
83 static void dump_launchers()
86 struct desc_list *desc;
87 struct type_list *type;
89 for (desc = launchers ; desc != NULL ; desc = desc->next) {
90 printf("mode %s\n", name_of_launch_mode(desc->mode));
91 for (type = desc->types ; type != NULL ; type = type->next)
92 printf("%s\n", type->type);
93 for ( j = 0 ; j < 2 ; j++)
94 if (desc->execs[j].args != NULL) {
95 for (k = 0 ; desc->execs[j].args[k] != NULL ; k++)
96 printf(" %s", desc->execs[j].args[k]);
103 static int next_token(struct confread *cread)
105 int idx = cread->index + cread->length;
106 cread->index = idx + strspn(&cread->buffer[idx], separators);
107 cread->length = strcspn(&cread->buffer[cread->index], separators);
108 return cread->length;
111 static int read_line(struct confread *cread)
113 while (fgets(cread->buffer, sizeof cread->buffer, cread->file) != NULL) {
115 cread->index = strspn(cread->buffer, separators);
116 if (cread->buffer[cread->index] && cread->buffer[cread->index] != '#') {
117 cread->length = strcspn(&cread->buffer[cread->index], separators);
118 assert(cread->length > 0);
119 return cread->length;
122 if (ferror(cread->file)) {
123 ERROR("%s:%d: error while reading, %m", cread->filepath, cread->lineno);
129 static const char **read_vector(struct confread *cread)
137 index0 = cread->index;
138 length0 = cread->length;
143 while(cread->length) {
145 length += cread->length;
150 cread->index = index0;
151 cread->length = length0;
152 vector = malloc(length + count + (count + 1) * sizeof(char*));
157 args = (char*)(vector + count + 1);
159 while(cread->length) {
160 vector[count++] = args;
161 memcpy(args, &cread->buffer[cread->index], cread->length);
162 args += cread->length;
166 vector[count] = NULL;
167 cread->index = index0;
168 cread->length = length0;
172 static struct type_list *read_type(struct confread *cread)
175 struct type_list *result;
177 /* record index and length */
178 index = cread->index;
179 length = cread->length;
181 /* check no extra characters */
182 if (next_token(cread)) {
183 ERROR("%s:%d: extra characters found after type %.*s",
184 cread->filepath, cread->lineno, length, &cread->buffer[index]);
189 /* allocate structure */
190 result = malloc(sizeof(struct type_list) + length);
191 if (result == NULL) {
192 ERROR("%s:%d: out of memory", cread->filepath, cread->lineno);
197 /* fill the structure */
198 memcpy(result->type, &cread->buffer[index], length);
199 result->type[length] = 0;
203 static enum afm_launch_mode read_mode(struct confread *cread)
206 enum afm_launch_mode result;
208 assert(cread->index == 0);
209 assert(!strncmp(&cread->buffer[cread->index], "mode", 4));
211 /* get the next token: the mode string */
212 if (!next_token(cread)) {
213 ERROR("%s:%d: no mode value set", cread->filepath, cread->lineno);
215 return invalid_launch_mode;
218 /* record index and length */
219 index = cread->index;
220 length = cread->length;
222 /* check no extra characters */
223 if (next_token(cread)) {
224 ERROR("%s:%d: extra characters found after mode %.*s",
225 cread->filepath, cread->lineno, length, &cread->buffer[index]);
227 return invalid_launch_mode;
231 cread->buffer[index + length] = 0;
232 result = launch_mode_of_string(&cread->buffer[index]);
233 if (result == invalid_launch_mode) {
234 ERROR("%s:%d: invalid mode value %s",
235 cread->filepath, cread->lineno, &cread->buffer[index]);
241 static void free_type_list(struct type_list *types)
243 while (types != NULL) {
244 struct type_list *next = types->next;
250 static int read_launchers(struct confread *cread)
253 struct type_list *types, *lt;
254 struct desc_list *desc;
255 enum afm_launch_mode mode;
262 mode = invalid_launch_mode;
263 rc = read_line(cread);
265 if (cread->index == 0) {
266 if (cread->length == 4
267 && !memcmp(&cread->buffer[cread->index], "mode", 4)) {
268 /* check if allowed */
270 ERROR("%s:%d: mode found before launch vector",
271 cread->filepath, cread->lineno);
273 free_type_list(types);
278 mode = read_mode(cread);
279 if (mode == invalid_launch_mode)
282 if (mode == invalid_launch_mode) {
283 ERROR("%s:%d: mode not found before type",
284 cread->filepath, cread->lineno);
286 assert(types == NULL);
290 lt = read_type(cread);
292 free_type_list(types);
299 } else if (types == NULL && desc == NULL) {
301 ERROR("%s:%d: untyped launch vector found",
302 cread->filepath, cread->lineno);
304 ERROR("%s:%d: extra launch vector found (2 max)",
305 cread->filepath, cread->lineno);
309 has_readyfd = NULL != strstr(&cread->buffer[cread->index], "%R");
310 vector = read_vector(cread);
311 if (vector == NULL) {
312 ERROR("%s:%d: out of memory",
313 cread->filepath, cread->lineno);
314 free_type_list(types);
319 assert(desc == NULL);
320 desc = malloc(sizeof * desc);
322 ERROR("%s:%d: out of memory",
323 cread->filepath, cread->lineno);
324 free_type_list(types);
328 desc->next = launchers;
331 desc->execs[0].has_readyfd = has_readyfd;
332 desc->execs[0].args = vector;
333 desc->execs[1].has_readyfd = 0;
334 desc->execs[1].args = NULL;
338 desc->execs[1].has_readyfd = has_readyfd;
339 desc->execs[1].args = vector;
343 rc = read_line(cread);
346 ERROR("%s:%d: end of file found before launch vector",
347 cread->filepath, cread->lineno);
348 free_type_list(types);
355 static int read_configuration_file(const char *filepath)
358 struct confread cread;
360 /* opens the configuration file */
361 cread.file = fopen(filepath, "r");
362 if (cread.file == NULL) {
364 ERROR("can't read file %s: %m", filepath);
368 cread.filepath = filepath;
370 rc = read_launchers(&cread);
379 %c content desc->content
380 %D datadir params->datadir
381 %H height desc->height
382 %h homedir desc->home
383 %I icondir FWK_ICON_DIR
384 %m mime-type desc->type
386 %p plugins desc->plugins
388 %r rootdir desc->path
389 %R readyfd params->readyfd
390 %S secret params->secret
391 %t tag (smack label) desc->tag
400 static union arguments instantiate_arguments(
401 const char * const *args,
402 struct afm_launch_desc *desc,
403 struct launchparam *params,
407 const char * const *iter;
409 char *data, port[20], width[20], height[20], readyfd[20], mini[3], c, sep;
411 union arguments result;
414 sep = wants_vector ? 0 : ' ';
418 /* loop that either compute the size and build the result */
419 result.vector = NULL;
420 result.scalar = NULL;
429 result.vector[n] = data;
431 while((c = *p++) != 0) {
440 case 'a': v = desc->appid; break;
441 case 'c': v = desc->content; break;
442 case 'D': v = params->datadir; break;
445 sprintf(height, "%d", desc->height);
448 case 'h': v = desc->home; break;
449 case 'I': v = FWK_ICON_DIR; break;
450 case 'm': v = desc->type; break;
451 case 'n': v = desc->name; break;
454 sprintf(port, "%d", params->port);
457 case 'p': v = "" /*desc->plugins*/; break;
460 sprintf(readyfd, "%d", params->readyfd);
463 case 'r': v = desc->path; break;
464 case 'S': v = params->secret; break;
465 case 't': v = desc->tag; break;
468 sprintf(width, "%d", desc->width);
479 data = stpcpy(data, v);
490 assert(!wants_vector);
496 result.scalar = malloc(s);
497 if (result.scalar == NULL) {
501 data = result.scalar;
503 assert(wants_vector);
505 result.vector[n] = NULL;
509 result.vector = malloc((n+1)*sizeof(char*) + s);
510 if (result.vector == NULL) {
514 data = (char*)(&result.vector[n + 1]);
519 static void mksecret(char buffer[9])
521 snprintf(buffer, 9, "%08lX", (0xffffffff & random()));
526 static int port_ring = 12345;
527 int port = port_ring;
528 if (port < 12345 || port > 15432)
530 port_ring = port + 1;
534 static int launch_local_1(
535 struct afm_launch_desc *desc,
537 struct launchparam *params
543 /* fork the master child */
544 children[0] = fork();
545 if (children[0] < 0) {
546 ERROR("master fork failed: %m");
550 /********* in the parent process ************/
554 /********* in the master child ************/
556 /* avoid set-gid effect */
557 setresgid(groupid, groupid, groupid);
559 /* enter the process group */
562 ERROR("setpgid failed");
566 /* enter security mode */
567 rc = secmgr_prepare_exec(desc->tag);
569 ERROR("call to secmgr_prepare_exec failed: %m");
573 /* enter the datadirectory */
574 rc = mkdir(params->datadir, 0755);
575 if (rc && errno != EEXIST) {
576 ERROR("creation of datadir %s failed: %m", params->datadir);
579 rc = chdir(params->datadir);
581 ERROR("can't enter the datadir %s: %m", params->datadir);
585 args = instantiate_arguments(params->master->args, desc, params, 1).vector;
587 ERROR("out of memory in master");
590 rc = execve(args[0], args, environ);
591 ERROR("failed to exec master %s: %m", args[0]);
596 static int launch_local_2(
597 struct afm_launch_desc *desc,
599 struct launchparam *params
608 /* prepare the pipes */
609 rc = pipe2(mpipe, O_CLOEXEC);
611 ERROR("error while calling pipe2: %m");
614 rc = pipe2(spipe, O_CLOEXEC);
616 ERROR("error while calling pipe2: %m");
622 /* fork the master child */
623 children[0] = fork();
624 if (children[0] < 0) {
625 ERROR("master fork failed: %m");
633 /********* in the parent process ************/
636 /* wait the ready signal (that transmit the slave pid) */
637 rc = read(mpipe[0], &children[1], sizeof children[1]);
640 ERROR("reading master pipe failed: %m");
644 assert(rc == sizeof children[1]);
645 /* start the child */
646 rc = write(spipe[1], "start", 5);
648 ERROR("writing slave pipe failed: %m");
657 /********* in the master child ************/
661 /* avoid set-gid effect */
662 setresgid(groupid, groupid, groupid);
664 /* enter the process group */
667 ERROR("setpgid failed");
671 /* enter security mode */
672 rc = secmgr_prepare_exec(desc->tag);
674 ERROR("call to secmgr_prepare_exec failed: %m");
678 /* enter the datadirectory */
679 rc = mkdir(params->datadir, 0755);
680 if (rc && errno != EEXIST) {
681 ERROR("creation of datadir %s failed: %m", params->datadir);
684 rc = chdir(params->datadir);
686 ERROR("can't enter the datadir %s: %m", params->datadir);
690 /* fork the slave child */
691 children[1] = fork();
692 if (children[1] < 0) {
693 ERROR("slave fork failed: %m");
696 if (children[1] == 0) {
697 /********* in the slave child ************/
699 rc = read(spipe[0], message, sizeof message);
701 ERROR("reading slave pipe failed: %m");
705 args = instantiate_arguments(params->slave->args, desc, params, 1).vector;
707 ERROR("out of memory in slave");
710 rc = execve(args[0], args, environ);
711 ERROR("failed to exec slave %s: %m", args[0]);
716 /********* still in the master child ************/
718 args = instantiate_arguments(params->master->args, desc, params, 1).vector;
720 ERROR("out of memory in master");
723 rc = write(mpipe[1], &children[1], sizeof children[1]);
725 ERROR("can't write master pipe: %m");
729 rc = execve(args[0], args, environ);
730 ERROR("failed to exec master %s: %m", args[0]);
736 static int launch_local(
737 struct afm_launch_desc *desc,
739 struct launchparam *params
742 if (params->slave == NULL)
743 return launch_local_1(desc, children, params);
744 return launch_local_2(desc, children, params);
747 static int launch_remote(
748 struct afm_launch_desc *desc,
750 struct launchparam *params
756 /* instanciate the uri */
757 if (params->slave == NULL)
760 uri = instantiate_arguments(params->slave->args, desc, params, 0).scalar;
762 ERROR("out of memory for remote uri");
767 /* launch the command */
768 rc = launch_local_1(desc, children, params);
776 static struct desc_list *search_launcher(const char *type, enum afm_launch_mode mode)
778 struct desc_list *dl;
779 struct type_list *tl;
781 for (dl = launchers ; dl ; dl = dl->next)
782 if (dl->mode == mode)
783 for (tl = dl->types ; tl != NULL ; tl = tl->next)
784 if (!strcmp(tl->type, type))
789 int afm_launch(struct afm_launch_desc *desc, pid_t children[2], char **uri)
792 char datadir[PATH_MAX];
794 struct launchparam params;
796 struct desc_list *dl;
799 assert(groupid != 0);
800 assert(launch_mode_is_valid(desc->mode));
801 assert(desc->mode == mode_local || uri != NULL);
802 assert(uri == NULL || *uri == NULL);
808 /* what launcher ? */
809 type = desc->type != NULL && *desc->type ? desc->type : DEFAULT_TYPE;
810 dl = search_launcher(type, desc->mode);
812 ERROR("type %s not found for mode %s!", type, name_of_launch_mode(desc->mode));
818 rc = snprintf(datadir, sizeof datadir, "%s/%s", desc->home, desc->tag);
819 if (rc < 0 || rc >= sizeof datadir) {
820 ERROR("overflow for datadir");
825 /* make the secret and port */
828 params.port = mkport();
829 params.secret = secret;
830 params.datadir = datadir;
831 params.master = &dl->execs[0];
832 params.slave = &dl->execs[1];
834 switch (desc->mode) {
836 return launch_local(desc, children, ¶ms);
838 return launch_remote(desc, children, ¶ms);
845 int afm_launch_initialize()
850 getresgid(&r, &e, &s);
856 rc = read_configuration_file(FWK_LAUNCH_CONF);