/[vps]/baselayout-vserver/trunk/src/core/parse.c
Gentoo

Diff of /baselayout-vserver/trunk/src/core/parse.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 126 Revision 127
1/*
2 * parse.c
3 *
4 * Parser for Gentoo style rc-scripts.
5 *
6 * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * $Header$
23 */
24
25#include <errno.h>
26#include <string.h>
27#include <stddef.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <signal.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <sys/wait.h>
34#include <sys/poll.h>
35#include <unistd.h>
36#include <fcntl.h>
37#include <signal.h>
38
39#include "debug.h"
40#include "depend.h"
41#include "list.h"
42#include "misc.h"
43#include "parse.h"
44#include "simple-regex.h"
45
46#define READ_PIPE 0
47#define WRITE_PIPE 1
48
49/* _pipe[0] is used to send data to the parent (thus the parent only use the
50 * read pipe, and the child uses the write pipe)
51 * _pipe[1] is used to send data to the child (thus the child only use the read
52 * pipe, and the parent uses the write pipe)
53 */
54#define PARENT_READ_PIPE(_pipe) (_pipe[0][READ_PIPE])
55#define PARENT_WRITE_PIPE(_pipe) (_pipe[1][WRITE_PIPE])
56#define CHILD_READ_PIPE(_pipe) (_pipe[1][READ_PIPE])
57#define CHILD_WRITE_PIPE(_pipe) (_pipe[0][WRITE_PIPE])
58
59#define PARSE_BUFFER_SIZE 256
60
61#define OUTPUT_MAX_LINE_LENGHT 256
62#define OUTPUT_BUFFER_SIZE (60 * 2048)
63
64/* void PRINT_TO_BUFFER(char **_buf, int _count, label _error, format) */
65#define PRINT_TO_BUFFER(_buf, _count, _error, _output...) \
66 do { \
67 int _i = 0; \
68 /* FIXME: Might do something more dynamic here */ \
69 if (OUTPUT_BUFFER_SIZE < (_count + OUTPUT_MAX_LINE_LENGHT)) { \
70 errno = ENOMEM; \
71 DBG_MSG("Output buffer size too small!\n"); \
72 goto _error; \
73 } \
74 _i = sprintf(&((*_buf)[_count]), _output); \
75 if (0 < _i) \
76 _count += _i + 1; \
77 } while (0)
78
79LIST_HEAD(rcscript_list);
80
81size_t parse_rcscript(char *scriptname, char **data, size_t index);
82
83size_t parse_print_start(char **data, size_t index);
84size_t parse_print_header(char *scriptname, char **data, size_t index);
85size_t parse_print_body(char *scriptname, char **data, size_t index);
86
87int get_rcscripts(void)
88{
89 rcscript_info_t *info;
90 char **file_list = NULL;
91 char *rcscript;
92 char *confd_file = NULL;
93 int count;
94
95 file_list = ls_dir(INITD_DIR_NAME, 0);
96 if (NULL == file_list) {
97 DBG_MSG("'%s' is empty!\n", INITD_DIR_NAME);
98 return -1;
99 }
100
101 STRING_LIST_FOR_EACH(file_list, rcscript, count) {
102 /* Is it a file? */
103 if (!(is_file(rcscript, 1))
104 /* Do not process scripts, source or backup files. */
105 || (CHECK_FILE_EXTENSION(rcscript, ".c"))
106 || (CHECK_FILE_EXTENSION(rcscript, ".bak"))
107 || (CHECK_FILE_EXTENSION(rcscript, "~"))) {
108 DBG_MSG("'%s' is not a valid rc-script!\n",
109 gbasename(rcscript));
110 } else {
111 DBG_MSG("Adding rc-script '%s' to list.\n",
112 gbasename(rcscript));
113
114 info = malloc(sizeof(rcscript_info_t));
115 if (NULL == info) {
116 DBG_MSG("Failed to allocate rcscript_info_t!\n");
117 goto error;
118 }
119
120 /* Copy the name */
121 info->filename = strndup(rcscript, strlen(rcscript));
122 if (NULL == info->filename) {
123 DBG_MSG("Failed to allocate buffer!\n");
124 goto loop_error;
125 }
126
127 /* Get the modification time */
128 info->mtime = get_mtime(rcscript, 1);
129 if (0 == info->mtime) {
130 DBG_MSG("Failed to get modification time for '%s'!\n",
131 rcscript);
132 /* We do not care if it fails - we will pick up
133 * later if there is a problem with the file */
134 }
135
136 /* File name for the conf.d config file (if any) */
137 confd_file = strcatpaths(CONFD_DIR_NAME, gbasename(rcscript));
138 if (NULL == confd_file) {
139 DBG_MSG("Failed to allocate temporary buffer!\n");
140 goto loop_error;
141 }
142
143 /* Get the modification time of the conf.d file
144 * (if any exists) */
145 info->confd_mtime = get_mtime(confd_file, 1);
146 if (0 == info->confd_mtime) {
147 DBG_MSG("Failed to get modification time for '%s'!\n",
148 confd_file);
149 /* We do not care that it fails, as not all
150 * rc-scripts will have conf.d config files */
151 }
152
153 free(confd_file);
154
155 list_add_tail(&info->node, &rcscript_list);
156
157 continue;
158
159loop_error:
160 if (NULL != info)
161 free(info->filename);
162 free(info);
163
164 goto error;
165 }
166 }
167
168 /* Final check if we have some entries */
169 if ((NULL == file_list) || (NULL == file_list[0])) {
170 DBG_MSG("No rc-scripts to parse!\n");
171 errno = ENOENT;
172 goto error;
173 }
174
175 STRING_LIST_FREE(file_list);
176
177 return 0;
178
179error:
180 STRING_LIST_FREE(file_list);
181
182 return -1;
183}
184
185/* Returns 0 if we do not need to regen the cache file, else -1 with
186 * errno set if something went wrong */
187int check_rcscripts_mtime(char *cachefile)
188{
189 rcscript_info_t *info;
190 time_t cache_mtime;
191 time_t rc_conf_mtime;
192 time_t rc_confd_mtime;
193
194 if ((NULL == cachefile) || (0 == strlen(cachefile))) {
195 DBG_MSG("Invalid argument passed!\n");
196 errno = EINVAL;
197 return -1;
198 }
199
200 cache_mtime = get_mtime(cachefile, 1);
201 if (0 == cache_mtime) {
202 DBG_MSG("Could not get modification time for cache file '%s'!\n",
203 cachefile);
204 return -1;
205 }
206
207 /* Get and compare mtime for RC_CONF_FILE_NAME with that of cachefile */
208 rc_conf_mtime = get_mtime(RC_CONF_FILE_NAME, 1);
209 if (rc_conf_mtime > cache_mtime) {
210 DBG_MSG("'%s' have a later modification time than '%s'.\n",
211 RC_CONF_FILE_NAME, cachefile);
212 return -1;
213 }
214 /* Get and compare mtime for RC_CONFD_FILE_NAME with that of cachefile */
215 rc_confd_mtime = get_mtime(RC_CONFD_FILE_NAME, 1);
216 if (rc_confd_mtime > cache_mtime) {
217 DBG_MSG("'%s' have a later modification time than '%s'.\n",
218 RC_CONFD_FILE_NAME, cachefile);
219 return -1;
220 }
221
222 /* Get and compare mtime for each rc-script and its conf.d config file
223 * with that of cachefile */
224 list_for_each_entry(info, &rcscript_list, node) {
225 if ((info->mtime > cache_mtime)
226 || (info->confd_mtime > cache_mtime)) {
227 DBG_MSG("'%s' have a later modification time than '%s'.\n",
228 info->filename, cachefile);
229 return -1;
230 }
231 }
232
233 return 0;
234}
235
236/* Return count on success, -1 on error. If it was critical, errno will be set. */
237size_t parse_rcscript(char *scriptname, char **data, size_t index)
238{
239 regex_data_t tmp_data;
240 char *buf = NULL;
241 char *tmp_buf = NULL;
242 size_t write_count = index;
243 size_t lenght;
244 int count;
245 int current = 0;
246
247 if ((NULL == scriptname) || (0 == strlen(scriptname))) {
248 DBG_MSG("Invalid argument passed!\n");
249 errno = EINVAL;
250 return -1;
251 }
252
253 if (-1 == file_map(scriptname, &buf, &lenght)) {
254 DBG_MSG("Could not open '%s' for reading!\n",
255 gbasename(scriptname));
256 return -1;
257 }
258
259 while (current < lenght) {
260 count = buf_get_line(buf, lenght, current);
261
262 tmp_buf = strndup(&(buf[current]), count);
263 if (NULL == tmp_buf) {
264 DBG_MSG("Failed to allocate temporary buffer!\n");
265 goto error;
266 }
267
268 if (0 == current) {
269 /* Check if it starts with '#!/sbin/runscript' */
270 DO_REGEX(tmp_data, tmp_buf,
271 "[ \t]*#![ \t]*/sbin/runscript[ \t]*.*", error);
272 if (REGEX_FULL_MATCH != tmp_data.match) {
273 DBG_MSG("'%s' is not a valid rc-script!\n",
274 gbasename(scriptname));
275 goto error;
276 }
277
278 /* We do not want rc-scripts ending in '.sh' */
279 if (CHECK_FILE_EXTENSION(scriptname, ".sh")) {
280 EWARN("'%s' is invalid (should not end with '.sh')!\n",
281 gbasename(scriptname));
282 goto error;
283 }
284 DBG_MSG("Parsing '%s'.\n", gbasename(scriptname));
285
286 write_count = parse_print_header(gbasename(scriptname),
287 data, write_count);
288 if (-1 == write_count) {
289 DBG_MSG("Failed to call parse_print_header()!\n");
290 goto error;
291 }
292
293 goto _continue;
294 }
295
296 /* Check for lines with comments, and skip them */
297 DO_REGEX(tmp_data, tmp_buf, "^[ \t]*#", error);
298 if (REGEX_MATCH(tmp_data))
299 goto _continue;
300
301 /* If the line contains 'depend()', set 'got_depend' */
302 DO_REGEX(tmp_data, tmp_buf, "depend[ \t]*()[ \t]*{?", error);
303 if (REGEX_MATCH(tmp_data)) {
304 DBG_MSG("Got 'depend()' function.\n");
305
306 write_count = parse_print_body(gbasename(scriptname),
307 data, write_count);
308 if (-1 == write_count) {
309 DBG_MSG("Failed to call parse_print_body()!\n");
310 goto error;
311 }
312
313 /* Make sure this is the last loop */
314 current += lenght;
315 goto _continue;
316 }
317
318_continue:
319 current += count + 1;
320 free(tmp_buf);
321 }
322
323 file_unmap(buf, lenght);
324
325 return write_count;
326
327error:
328 free(tmp_buf);
329 if (NULL != buf) {
330 int old_errno = errno;
331 file_unmap(buf, lenght);
332 /* file_unmap() might have changed it */
333 errno = old_errno;
334 }
335
336 return -1;
337}
338
339
340size_t generate_stage1(char **data)
341{
342 rcscript_info_t *info;
343 size_t write_count = 0;
344 size_t tmp_count;
345
346 write_count = parse_print_start(data, write_count);
347 if (-1 == write_count) {
348 DBG_MSG("Failed to call parse_print_start()!\n");
349 return -1;
350 }
351
352 list_for_each_entry(info, &rcscript_list, node) {
353 tmp_count = parse_rcscript(info->filename, data, write_count);
354 if (-1 == tmp_count) {
355 DBG_MSG("Failed to parse '%s'!\n",
356 gbasename(info->filename));
357
358 /* If 'errno' is set, it is critical (hopefully) */
359 if (0 != errno)
360 return -1;
361 } else {
362 write_count = tmp_count;
363 }
364 }
365
366 return write_count;
367}
368
369/* Empty signal handler for SIGPIPE */
370static void sig_handler(int signum)
371{
372 return;
373}
374
375/* Returns data's lenght on success, else -1 on error. */
376size_t generate_stage2(char **data)
377{
378 int pipe_fds[2][2] = { { 0, 0 }, { 0, 0 } };
379 pid_t child_pid;
380 size_t write_count = 0;
381 int old_errno = 0;
382
383 /* Pipe to send data to parent */
384 if (-1 == pipe(pipe_fds[0])) {
385 DBG_MSG("Failed to open pipe!\n");
386 goto error;
387 }
388 /* Pipe to send data to child */
389 if (-1 == pipe(pipe_fds[1])) {
390 DBG_MSG("Failed to open pipe!\n");
391 /* Close parent_pfds */
392 goto error;
393 }
394
395 /* Zero data */
396 *data = NULL;
397
398 child_pid = fork();
399 if (-1 == child_pid) {
400 DBG_MSG("Failed to fork()!\n");
401 /* Close all pipes */
402 goto error;
403 }
404 if (0 == child_pid) {
405 /***
406 *** In child
407 ***/
408
409 char *const argv[] = {
410 "bash",
411 "--noprofile",
412 "--norc",
413 "--",
414 NULL
415 };
416
417 /* Close the sides of the pipes we do not use */
418 close(PARENT_WRITE_PIPE(pipe_fds));
419 close(PARENT_READ_PIPE(pipe_fds));
420
421 /* dup2 child side read pipe to STDIN */
422 dup2(CHILD_READ_PIPE(pipe_fds), STDIN_FILENO);
423 /* dup2 child side write pipe to STDOUT */
424 dup2(CHILD_WRITE_PIPE(pipe_fds), STDOUT_FILENO);
425
426 /* We need to be in INITD_DIR_NAME for 'before'/'after' '*' to work */
427 if (-1 == chdir(INITD_DIR_NAME)) {
428 DBG_MSG("Failed to chdir to '%s'!\n", INITD_DIR_NAME);
429 exit(1);
430 }
431
432 if (-1 == execv(SHELL_PARSER, argv)) {
433 DBG_MSG("Failed to execv %s!\n", SHELL_PARSER);
434 exit(1);
435 }
436 } else {
437 /***
438 *** In parent
439 ***/
440
441 struct sigaction act_new;
442 struct sigaction act_old;
443 struct pollfd poll_fds[2];
444 char buf[PARSE_BUFFER_SIZE+1];
445 char *stage1_data = NULL;
446 size_t stage1_write_count = 0;
447 size_t stage1_written = 0;
448 int status = 0;
449
450 DBG_MSG("Child pid = %i\n", child_pid);
451
452 /* Set signal handler for SIGPIPE to empty in case bash errors
453 * out. It will then close the write pipe, and instead of us
454 * getting SIGPIPE, we can handle the write error like normal.
455 */
456 memset(&act_new, 0x00, sizeof(act_new));
457 act_new.sa_handler = (void (*) (int))sig_handler;
458 sigemptyset (&act_new.sa_mask);
459 act_new.sa_flags = 0;
460 sigaction(SIGPIPE, &act_new, &act_old);
461
462 /* Close the sides of the pipes we do not use */
463 close(CHILD_WRITE_PIPE(pipe_fds));
464 CHILD_WRITE_PIPE(pipe_fds) = 0;
465 close(CHILD_READ_PIPE(pipe_fds));
466 CHILD_READ_PIPE(pipe_fds) = 0;
467
468 stage1_data = malloc(OUTPUT_BUFFER_SIZE + 1);
469 if (NULL == stage1_data) {
470 DBG_MSG("Failed to allocate buffer!\n");
471 goto error;
472 }
473
474 /* Pipe parse_rcscripts() to bash */
475 stage1_write_count = generate_stage1(&stage1_data);
476 if (-1 == stage1_write_count) {
477 DBG_MSG("Failed to generate stage1!\n");
478 goto error;
479 }
480
481#if 0
482 int tmp_fd = open("bar", O_CREAT | O_TRUNC | O_RDWR, 0600);
483 write(tmp_fd, stage1_data, stage1_write_count);
484 close(tmp_fd);
485#endif
486
487 do {
488 int tmp_count = 0;
489 int do_write = 0;
490 int do_read = 0;
491
492 /* Check if we can write or read */
493 poll_fds[WRITE_PIPE].fd = PARENT_WRITE_PIPE(pipe_fds);
494 poll_fds[WRITE_PIPE].events = POLLOUT;
495 poll_fds[READ_PIPE].fd = PARENT_READ_PIPE(pipe_fds);
496 poll_fds[READ_PIPE].events = POLLIN | POLLPRI;
497 if (stage1_written < stage1_write_count) {
498 poll(poll_fds, 2, -1);
499 if (poll_fds[WRITE_PIPE].revents & POLLOUT)
500 do_write = 1;
501 } else {
502 poll(&(poll_fds[READ_PIPE]), 1, -1);
503 }
504 if ((poll_fds[READ_PIPE].revents & POLLIN)
505 || (poll_fds[READ_PIPE].revents & POLLPRI))
506 do_read = 1;
507
508 do {
509 /* If we can write, or there is nothing to
510 * read, keep feeding the write pipe */
511 if ((stage1_written >= stage1_write_count)
512 || (1 == do_read)
513 || (1 != do_write))
514 break;
515
516 tmp_count = write(PARENT_WRITE_PIPE(pipe_fds),
517 &stage1_data[stage1_written],
518 strlen(&stage1_data[stage1_written]));
519 if ((-1 == tmp_count) && (EINTR != errno)) {
520 DBG_MSG("Error writing to PARENT_WRITE_PIPE!\n");
521 goto failed;
522 }
523 /* We were interrupted, try to write again */
524 if (-1 == tmp_count) {
525 errno = 0;
526 /* Make sure we retry */
527 tmp_count = 1;
528 continue;
529 }
530 /* What was written before, plus what
531 * we wrote now as well as the ending
532 * '\0' of the line */
533 stage1_written += tmp_count + 1;
534
535 /* Close the write pipe if we done
536 * writing to get a EOF signaled to
537 * bash */
538 if (stage1_written >= stage1_write_count) {
539 close(PARENT_WRITE_PIPE(pipe_fds));
540 PARENT_WRITE_PIPE(pipe_fds) = 0;
541 }
542 } while ((tmp_count > 0) && (stage1_written < stage1_write_count));
543
544 /* Reset tmp_count for below read loop */
545 tmp_count = 0;
546
547 do {
548 char *tmp_p;
549
550 if (1 != do_read)
551 continue;
552
553 tmp_count = read(PARENT_READ_PIPE(pipe_fds), buf,
554 PARSE_BUFFER_SIZE);
555 if ((-1 == tmp_count) && (EINTR != errno)) {
556 DBG_MSG("Error reading PARENT_READ_PIPE!\n");
557 goto failed;
558 }
559 /* We were interrupted, try to read again */
560 if ((-1 == tmp_count) || (0 == tmp_count)) {
561 errno = 0;
562 continue;
563 }
564
565 tmp_p = realloc(*data, write_count + tmp_count);
566 if (NULL == tmp_p) {
567 DBG_MSG("Failed to allocate buffer!\n");
568 goto failed;
569 }
570
571 memcpy(&tmp_p[write_count], buf, tmp_count);
572
573 *data = tmp_p;
574 write_count += tmp_count;
575 } while (tmp_count > 0);
576 } while (!(poll_fds[READ_PIPE].revents & POLLHUP));
577
578failed:
579 /* Set old_errno to disable child exit code checking below */
580 if (0 != errno)
581 old_errno = errno;
582
583 free(stage1_data);
584
585 if (0 != PARENT_WRITE_PIPE(pipe_fds))
586 close(PARENT_WRITE_PIPE(pipe_fds));
587 close(PARENT_READ_PIPE(pipe_fds));
588
589 /* Restore the old signal handler for SIGPIPE */
590 sigaction(SIGPIPE, &act_old, NULL);
591
592 /* Wait for bash to finish */
593 waitpid(child_pid, &status, 0);
594 /* If old_errno is set, we had an error in the read loop, so do
595 * not worry about the child's exit code */
596 if (0 == old_errno) {
597 if ((!WIFEXITED(status)) || (0 != WEXITSTATUS(status))) {
598 DBG_MSG("Bash failed with status 0x%x!\n", status);
599 return -1;
600 }
601 } else {
602 /* Right, we had an error, so set errno, and exit */
603 errno = old_errno;
604 return -1;
605 }
606 }
607
608 return write_count;
609
610 /* Close parent side pipes */
611error:
612 /* Close all pipes */
613 old_errno = errno;
614 if (0 != CHILD_READ_PIPE(pipe_fds))
615 close(CHILD_READ_PIPE(pipe_fds));
616 if (0 != CHILD_WRITE_PIPE(pipe_fds))
617 close(CHILD_WRITE_PIPE(pipe_fds));
618 if (0 != PARENT_READ_PIPE(pipe_fds))
619 close(PARENT_READ_PIPE(pipe_fds));
620 if (0 != PARENT_WRITE_PIPE(pipe_fds))
621 close(PARENT_WRITE_PIPE(pipe_fds));
622 /* close() might have changed it */
623 errno = old_errno;
624
625 return -1;
626}
627
628int write_legacy_stage3(FILE *output)
629{
630 service_info_t *info;
631 char *service;
632 int count;
633 int index = 0;
634 int dep_count;
635 int i;
636
637 if (-1 == fileno(output)) {
638 DBG_MSG("Bad output stream!\n");
639 return -1;
640 }
641
642 fprintf(output, "rc_type_ineed=2\n");
643 fprintf(output, "rc_type_needsme=3\n");
644 fprintf(output, "rc_type_iuse=4\n");
645 fprintf(output, "rc_type_usesme=5\n");
646 fprintf(output, "rc_type_ibefore=6\n");
647 fprintf(output, "rc_type_iafter=7\n");
648 fprintf(output, "rc_type_broken=8\n");
649 fprintf(output, "rc_type_mtime=9\n");
650 fprintf(output, "rc_index_scale=10\n\n");
651 fprintf(output, "declare -a RC_DEPEND_TREE\n\n");
652
653 list_for_each_entry(info, &service_info_list, node) {
654 index++;
655 }
656 if (0 == index) {
657 EERROR("No services to generate dependency tree for!\n");
658 return -1;
659 }
660
661 fprintf(output, "RC_DEPEND_TREE[0]=%i\n\n", index);
662
663 index = 1;
664
665 list_for_each_entry(info, &service_info_list, node) {
666 fprintf(output, "RC_DEPEND_TREE[%i]=\"%s\"\n",
667 index * 10, info->name);
668
669 for (i = 0;i <= BROKEN;i++) {
670 dep_count = 0;
671
672 fprintf(output, "RC_DEPEND_TREE[%i+%i]=",
673 (index * 10), (i + 2));
674
675 STRING_LIST_FOR_EACH(info->depend_info[i], service, count) {
676 if (0 == dep_count)
677 fprintf(output, "\"%s", service);
678 else
679 fprintf(output, " %s", service);
680
681 dep_count++;
682 }
683
684 if (dep_count > 0)
685 fprintf(output, "\"\n");
686 else
687 fprintf(output, "\n");
688 }
689
690 fprintf(output, "RC_DEPEND_TREE[%i+9]=\"%li\"\n\n",
691 index * 10, info->mtime);
692 index++;
693 }
694
695 fprintf(output, "RC_GOT_DEPTREE_INFO=\"yes\"\n");
696
697 info = service_get_virtual("logger");
698 if (NULL == info) {
699 DBG_MSG("No service provides the 'logger' logger virtual!\n");
700 fprintf(output, "\nLOGGER_SERVICE=\n");
701 } else {
702 fprintf(output, "\nLOGGER_SERVICE=\"%s\"\n", info->name);
703 }
704
705
706 return 0;
707}
708
709int parse_cache(const char *data, size_t lenght)
710{
711 service_info_t *info;
712 service_type_t type = ALL_SERVICE_TYPE_T;
713 rcscript_info_t *rs_info;
714 char *tmp_buf = NULL;
715 char *rc_name = NULL;
716 char *tmp_p;
717 char *token;
718 char *field;
719 int count;
720 int current = 0;
721 int retval;
722
723 if ((NULL == data) || (lenght <= 0)) {
724 DBG_MSG("Invalid argument passed!\n");
725 errno = EINVAL;
726 goto error;
727 }
728
729 while (current < lenght) {
730 count = buf_get_line((char *)data, lenght, current);
731
732 tmp_buf = strndup(&(data[current]), count);
733 if (NULL == tmp_buf) {
734 DBG_MSG("Failed to allocate temporary buffer!\n");
735 goto error;
736 }
737 tmp_p = tmp_buf;
738
739 /* Strip leading spaces/tabs */
740 while ((tmp_p[0] == ' ') || (tmp_p[0] == '\t'))
741 tmp_p++;
742
743 /* Get FIELD name and FIELD value */
744 token = strsep(&tmp_p, " ");
745
746 /* FIELD name empty/bogus? */
747 if ((NULL == token)
748 || (0 == strlen(token))
749 /* We got an empty FIELD value */
750 || (NULL == tmp_p)
751 || (0 == strlen(tmp_p))) {
752 DBG_MSG("Parsing stopped due to short read!\n");
753 errno = EMSGSIZE;
754 goto error;
755 }
756
757 if (0 == strcmp(token, FIELD_RCSCRIPT)) {
758 DBG_MSG("Field = '%s', value = '%s'\n", token, tmp_p);
759
760 /* Add the service to the list, and initialize all data */
761 retval = service_add(tmp_p);
762 if (-1 == retval) {
763 DBG_MSG("Failed to add %s to service list!\n", tmp_p);
764 goto error;
765 }
766
767 info = service_get_info(tmp_p);
768 if (NULL == info) {
769 DBG_MSG("Failed to get info for '%s'!\n", tmp_p);
770 goto error;
771 }
772 /* Save the rc-script name for next passes of loop */
773 rc_name = info->name;
774
775 goto _continue;
776 }
777
778 if (NULL == rc_name) {
779 DBG_MSG("Other fields should come after '%s'!\n", FIELD_RCSCRIPT);
780 goto error;
781 }
782
783 if (0 == strcmp(token, FIELD_NEED))
784 type = NEED;
785 else if (0 == strcmp(token, FIELD_USE))
786 type = USE;
787 else if (0 == strcmp(token, FIELD_BEFORE))
788 type = BEFORE;
789 else if (0 == strcmp(token, FIELD_AFTER))
790 type = AFTER;
791 else if (0 == strcmp(token, FIELD_PROVIDE))
792 type = PROVIDE;
793 else if (0 == strcmp(token, FIELD_FAILED)) {
794 type = BROKEN;
795
796 /* FIXME: Need to think about what to do syntax BROKEN
797 * services */
798 EWARN("'%s' has syntax errors, please correct!\n", rc_name);
799 }
800
801 if (type < ALL_SERVICE_TYPE_T) {
802 /* Get the first value *
803 * As the values are passed to a bash function, and we
804 * then use 'echo $*' to parse them, they should only
805 * have one space between each value ... */
806 token = strsep(&tmp_p, " ");
807
808 /* Get the correct type name */
809 field = service_type_names[type];
810
811 while (NULL != token) {
812 DBG_MSG("Field = '%s', service = '%s', value = '%s'\n",
813 field, rc_name, token);
814
815 retval = service_add_dependency(rc_name, token, type);
816 if (-1 == retval) {
817 DBG_MSG("Failed to add dependency '%s' to service '%s', type '%s'!\n",
818 token, rc_name, field);
819 goto error;
820 }
821
822 /* Get the next value (if any) */
823 token = strsep(&tmp_p, " ");
824 }
825
826 goto _continue;
827 }
828
829 /* Fall through */
830 DBG_MSG("Unknown FIELD in data!\n");
831
832_continue:
833 type = ALL_SERVICE_TYPE_T;
834 current += count + 1;
835 free(tmp_buf);
836 /* Do not free 'rc_name', as it should be consistant
837 * across loops */
838 }
839
840 /* Set the mtimes
841 * FIXME: Can drop this when we no longer need write_legacy_stage3() */
842 list_for_each_entry(rs_info, &rcscript_list, node) {
843 rc_name = gbasename(rs_info->filename);
844 if (NULL == service_get_info(rc_name))
845 continue;
846
847 retval = service_set_mtime(rc_name, rs_info->mtime);
848 if (-1 == retval) {
849 DBG_MSG("Failed to set mtime for service '%s'!\n", rc_name);
850 return -1;
851 }
852 }
853
854 return 0;
855
856error:
857 free(tmp_buf);
858
859 return -1;
860}
861
862size_t parse_print_start(char **data, size_t index)
863{
864 size_t write_count = index;
865
866 PRINT_TO_BUFFER(data, write_count, error,
867 ". /sbin/functions.sh\n"
868 "[ -e /etc/rc.conf ] && . /etc/rc.conf\n"
869 "\n"
870 /* "set -e\n" */
871 "\n");
872
873 return write_count;
874
875error:
876 return -1;
877}
878
879size_t parse_print_header(char *scriptname, char **data, size_t index)
880{
881 size_t write_count = index;
882
883 PRINT_TO_BUFFER(data, write_count, error,
884 "#*** %s ***\n"
885 "\n"
886 "myservice=\"%s\"\n"
887 "echo \"RCSCRIPT ${myservice}\"\n"
888 "\n",
889 scriptname, scriptname);
890
891 return write_count;
892
893error:
894 return -1;
895}
896
897size_t parse_print_body(char *scriptname, char **data, size_t index)
898{
899 size_t write_count = index;
900 char *tmp_buf = NULL;
901 char *tmp_ptr;
902 char *base;
903 char *ext;
904
905 tmp_buf = strndup(scriptname, strlen(scriptname));
906 if (NULL == tmp_buf) {
907 DBG_MSG("Failed to allocate temporary buffer!\n");
908 goto error;
909 }
910
911 /*
912 * Rather do the next block in C than bash, in case we want to
913 * use ash or another shell in the place of bash
914 */
915
916 /* bash: base="${myservice%%.*}" */
917 base = tmp_buf;
918 tmp_ptr = strchr(tmp_buf, '.');
919 if (NULL != tmp_ptr) {
920 tmp_ptr[0] = '\0';
921 tmp_ptr++;
922 } else {
923 tmp_ptr = tmp_buf;
924 }
925 /* bash: ext="${myservice##*.}" */
926 ext = strrchr(tmp_ptr, '.');
927 if (NULL == ext)
928 ext = tmp_ptr;
929
930 PRINT_TO_BUFFER(data, write_count, error,
931 "\n"
932 "(\n"
933 " # Get settings for rc-script ...\n"
934 " [ -e \"/etc/conf.d/${myservice}\" ] && \\\n"
935 " . \"/etc/conf.d/${myservice}\"\n"
936 " [ -e /etc/conf.d/net ] && \\\n"
937 " [ \"%s\" = \"net\" ] && \\\n"
938 " [ \"%s\" != \"${myservice}\" ] && \\\n"
939 " . /etc/conf.d/net\n"
940 " depend() {\n"
941 " return 0\n"
942 " }\n"
943 " \n"
944 " # Actual depend() function ...\n"
945 " (\n"
946 " set -e\n"
947 " . \"/etc/init.d/%s\" >/dev/null 2>&1\n"
948 " set +e\n"
949 " \n"
950 " need() {\n"
951 " [ \"$#\" -gt 0 ] && echo \"NEED $*\"; return 0\n"
952 " }\n"
953 " \n"
954 " use() {\n"
955 " [ \"$#\" -gt 0 ] && echo \"USE $*\"; return 0\n"
956 " }\n"
957 " \n"
958 " before() {\n"
959 " [ \"$#\" -gt 0 ] && echo \"BEFORE $*\"; return 0\n"
960 " }\n"
961 " \n"
962 " after() {\n"
963 " [ \"$#\" -gt 0 ] && echo \"AFTER $*\"; return 0\n"
964 " }\n"
965 " \n"
966 " provide() {\n"
967 " [ \"$#\" -gt 0 ] && echo \"PROVIDE $*\"; return 0\n"
968 " }\n"
969 " \n"
970 " depend\n"
971 " ) || echo \"FAILED ${myservice}\"\n"
972 ")\n"
973 "\n\n",
974 base, ext, scriptname);
975
976 return write_count;
977
978error:
979 return -1;
980}
981

Legend:
Removed from v.126  
changed lines
  Added in v.127

  ViewVC Help
Powered by ViewVC 1.1.20