2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: client_util.c,v 1.34 2006/05/25 01:47:11 johnfranks Exp $
33 #include "client_util.h"
36 #include "glib-util.h"
37 #include "timestamp.h"
38 #include "pipespawn.h"
42 #include "amandates.h"
44 #define MAXMAXDUMPS 16
46 static int add_exclude(FILE *file_exclude, char *aexc, int verbose);
47 static int add_include(char *disk, char *device, FILE *file_include, char *ainc, int verbose);
48 static char *build_name(char *disk, char *exin, int verbose);
49 static char *get_name(char *diskname, char *exin, time_t t, int n);
59 char *dirname = amname_to_dirname(device);
60 newname = vstralloc(dirname, "/", name , NULL);
64 newname = stralloc(name);
69 /* GDestroyFunc for a hash table whose values are GSLists contianing malloc'd
72 destroy_slist_free_full(gpointer list) {
73 slist_free_full((GSList *)list, g_free);
84 char number[NUM_STR_SIZE];
88 ts = get_timestamp_from_time(t);
92 g_snprintf(number, SIZEOF(number), "%03d", n - 1);
94 filename = vstralloc(get_pname(), ".", diskname, ".", ts, number, ".",
109 char *filename = NULL;
110 char *afilename = NULL;
116 struct dirent *entry;
118 size_t match_len, d_name_len;
122 diskname = sanitise_filename(disk);
124 dbgdir = stralloc2(AMANDA_TMPDIR, "/");
125 if((d = opendir(AMANDA_TMPDIR)) == NULL) {
126 error(_("open debug directory \"%s\": %s"),
127 AMANDA_TMPDIR, strerror(errno));
130 test_name = get_name(diskname, exin,
131 curtime - (getconf_int(CNF_DEBUG_DAYS) * 24 * 60 * 60), 0);
132 match_len = strlen(get_pname()) + strlen(diskname) + 2;
133 while((entry = readdir(d)) != NULL) {
134 if(is_dot_or_dotdot(entry->d_name)) {
137 d_name_len = strlen(entry->d_name);
138 if(strncmp(test_name, entry->d_name, match_len) != 0
139 || d_name_len < match_len + 14 + 8
140 || strcmp(entry->d_name+ d_name_len - 7, exin) != 0) {
141 continue; /* not one of our files */
143 if(strcmp(entry->d_name, test_name) < 0) {
144 e = newvstralloc(e, dbgdir, entry->d_name, NULL);
145 (void) unlink(e); /* get rid of old file */
154 filename = get_name(diskname, exin, curtime, n);
155 afilename = newvstralloc(afilename, dbgdir, filename, NULL);
156 if((fd=open(afilename, O_WRONLY|O_CREAT|O_APPEND, 0600)) < 0){
164 } while(!afilename && n < 1000);
166 if(afilename == NULL) {
167 filename = get_name(diskname, exin, curtime, 0);
168 afilename = newvstralloc(afilename, dbgdir, filename, NULL);
169 quoted = quote_string(afilename);
170 dbprintf(_("Cannot create %s (%s)\n"), quoted, strerror(errno));
172 g_printf(_("ERROR [cannot create %s (%s)]\n"),
173 quoted, strerror(errno));
196 (void)verbose; /* Quiet unused parameter warning */
199 if(aexc[l-1] == '\n') {
203 file = quoted = quote_string(aexc);
205 file[strlen(file) - 1] = '\0';
208 g_fprintf(file_exclude, "%s\n", file);
225 (void)disk; /* Quiet unused parameter warning */
226 (void)device; /* Quiet unused parameter warning */
229 if(ainc[l-1] == '\n') {
233 if (strncmp(ainc, "./", 2) != 0) {
234 quoted = quote_string(ainc);
235 dbprintf(_("include must start with './' (%s)\n"), quoted);
237 g_printf(_("ERROR [include must start with './' (%s)]\n"), quoted);
242 char *incname = ainc+2;
245 set_root = set_root_privs(1);
246 /* Take as is if not root && many '/' */
247 if(!set_root && strchr(incname, '/')) {
248 file = quoted = quote_string(ainc);
250 file[strlen(file) - 1] = '\0';
253 g_fprintf(file_include, "%s\n", file);
264 cwd = g_get_current_dir();
265 if (chdir(device) != 0) {
266 error(_("Failed to chdir(%s): %s\n"), device, strerror(errno));
268 glob(incname, 0, NULL, &globbuf);
269 if (chdir(cwd) != 0) {
270 error(_("Failed to chdir(%s): %s\n"), cwd, strerror(errno));
274 nb_exp = globbuf.gl_pathc;
275 for (nb=0; nb < nb_exp; nb++) {
276 file = stralloc2("./", globbuf.gl_pathv[nb]);
277 quoted = quote_string(file);
279 file[strlen(file) - 1] = '\0';
282 g_fprintf(file_include, "%s\n", file);
304 if (dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
305 if (dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
307 if (nb_exclude == 0) return NULL;
309 if ((filename = build_name(dle->disk, "exclude", verbose)) != NULL) {
310 if ((file_exclude = fopen(filename,"w")) != NULL) {
312 if (dle->exclude_file) {
313 for(excl = dle->exclude_file->first; excl != NULL;
315 add_exclude(file_exclude, excl->name,
316 verbose && dle->exclude_optional == 0);
320 if (dle->exclude_list) {
321 for(excl = dle->exclude_list->first; excl != NULL;
323 char *exclname = fixup_relative(excl->name, dle->device);
324 if((exclude = fopen(exclname, "r")) != NULL) {
325 while ((aexc = agets(exclude)) != NULL) {
326 if (aexc[0] == '\0') {
330 add_exclude(file_exclude, aexc,
331 verbose && dle->exclude_optional == 0);
337 quoted = quote_string(exclname);
338 dbprintf(_("Can't open exclude file %s (%s)\n"),
339 quoted, strerror(errno));
340 if(verbose && (dle->exclude_optional == 0 ||
342 g_printf(_("ERROR [Can't open exclude file %s (%s)]\n"),
343 quoted, strerror(errno));
350 fclose(file_exclude);
352 quoted = quote_string(filename);
353 dbprintf(_("Can't create exclude file %s (%s)\n"),
354 quoted, strerror(errno));
356 g_printf(_("ERROR [Can't create exclude file %s (%s)]\n"),
357 quoted, strerror(errno));
380 if (dle->include_file) nb_include += dle->include_file->nb_element;
381 if (dle->include_list) nb_include += dle->include_list->nb_element;
383 if (nb_include == 0) return NULL;
385 if ((filename = build_name(dle->disk, "include", verbose)) != NULL) {
386 if ((file_include = fopen(filename,"w")) != NULL) {
388 if (dle->include_file) {
389 for (incl = dle->include_file->first; incl != NULL;
391 nb_exp += add_include(dle->disk, dle->device, file_include,
393 verbose && dle->include_optional == 0);
397 if (dle->include_list) {
398 for (incl = dle->include_list->first; incl != NULL;
400 char *inclname = fixup_relative(incl->name, dle->device);
401 if ((include = fopen(inclname, "r")) != NULL) {
402 while ((ainc = agets(include)) != NULL) {
403 if (ainc[0] == '\0') {
407 nb_exp += add_include(dle->disk, dle->device,
409 verbose && dle->include_optional == 0);
415 quoted = quote_string(inclname);
416 dbprintf(_("Can't open include file %s (%s)\n"),
417 quoted, strerror(errno));
418 if (verbose && (dle->include_optional == 0 ||
420 g_printf(_("ERROR [Can't open include file %s (%s)]\n"),
421 quoted, strerror(errno));
428 fclose(file_include);
430 quoted = quote_string(filename);
431 dbprintf(_("Can't create include file %s (%s)\n"),
432 quoted, strerror(errno));
434 g_printf(_("ERROR [Can't create include file %s (%s)]\n"),
435 quoted, strerror(errno));
442 quoted = quote_string(dle->disk);
443 dbprintf(_("No include for %s\n"), quoted);
444 if (verbose && dle->include_optional == 0) {
445 g_printf(_("ERROR [No include for %s]\n"), quoted);
469 while (tok != NULL) {
470 if(am_has_feature(fs, fe_options_auth)
471 && BSTRNCMP(tok,"auth=") == 0) {
472 if (dle->auth != NULL) {
473 quoted = quote_string(tok + 5);
474 dbprintf(_("multiple auth option %s\n"), quoted);
476 g_printf(_("ERROR [multiple auth option %s]\n"), quoted);
480 dle->auth = stralloc(&tok[5]);
482 else if(am_has_feature(fs, fe_options_bsd_auth)
483 && BSTRNCMP(tok, "bsd-auth") == 0) {
484 if (dle->auth != NULL) {
485 dbprintf(_("multiple auth option\n"));
487 g_printf(_("ERROR [multiple auth option]\n"));
490 dle->auth = stralloc("bsd");
492 else if (BSTRNCMP(tok, "compress-fast") == 0) {
493 if (dle->compress != COMP_NONE) {
494 dbprintf(_("multiple compress option\n"));
496 g_printf(_("ERROR [multiple compress option]\n"));
499 dle->compress = COMP_FAST;
501 else if (BSTRNCMP(tok, "compress-best") == 0) {
502 if (dle->compress != COMP_NONE) {
503 dbprintf(_("multiple compress option\n"));
505 g_printf(_("ERROR [multiple compress option]\n"));
508 dle->compress = COMP_BEST;
510 else if (BSTRNCMP(tok, "srvcomp-fast") == 0) {
511 if (dle->compress != COMP_NONE) {
512 dbprintf(_("multiple compress option\n"));
514 g_printf(_("ERROR [multiple compress option]\n"));
517 dle->compress = COMP_SERVER_FAST;
519 else if (BSTRNCMP(tok, "srvcomp-best") == 0) {
520 if (dle->compress != COMP_NONE) {
521 dbprintf(_("multiple compress option\n"));
523 g_printf(_("ERROR [multiple compress option]\n"));
526 dle->compress = COMP_SERVER_BEST;
528 else if (BSTRNCMP(tok, "srvcomp-cust=") == 0) {
529 if (dle->compress != COMP_NONE) {
530 dbprintf(_("multiple compress option\n"));
532 g_printf(_("ERROR [multiple compress option]\n"));
535 dle->compprog = stralloc(tok + SIZEOF("srvcomp-cust=") -1);
536 dle->compress = COMP_SERVER_CUST;
538 else if (BSTRNCMP(tok, "comp-cust=") == 0) {
539 if (dle->compress != COMP_NONE) {
540 dbprintf(_("multiple compress option\n"));
542 g_printf(_("ERROR [multiple compress option]\n"));
545 dle->compprog = stralloc(tok + SIZEOF("comp-cust=") -1);
546 dle->compress = COMP_CUST;
547 /* parse encryption options */
549 else if (BSTRNCMP(tok, "encrypt-serv-cust=") == 0) {
550 if (dle->encrypt != ENCRYPT_NONE) {
551 dbprintf(_("multiple encrypt option\n"));
553 g_printf(_("ERROR [multiple encrypt option]\n"));
556 dle->srv_encrypt = stralloc(tok + SIZEOF("encrypt-serv-cust=") -1);
557 dle->encrypt = ENCRYPT_SERV_CUST;
559 else if (BSTRNCMP(tok, "encrypt-cust=") == 0) {
560 if (dle->encrypt != ENCRYPT_NONE) {
561 dbprintf(_("multiple encrypt option\n"));
563 g_printf(_("ERROR [multiple encrypt option]\n"));
566 dle->clnt_encrypt= stralloc(tok + SIZEOF("encrypt-cust=") -1);
567 dle->encrypt = ENCRYPT_CUST;
569 else if (BSTRNCMP(tok, "server-decrypt-option=") == 0) {
570 dle->srv_decrypt_opt = stralloc(tok + SIZEOF("server-decrypt-option=") -1);
572 else if (BSTRNCMP(tok, "client-decrypt-option=") == 0) {
573 dle->clnt_decrypt_opt = stralloc(tok + SIZEOF("client-decrypt-option=") -1);
575 else if (BSTRNCMP(tok, "no-record") == 0) {
576 if (dle->record != 1) {
577 dbprintf(_("multiple no-record option\n"));
579 g_printf(_("ERROR [multiple no-record option]\n"));
584 else if (BSTRNCMP(tok, "index") == 0) {
585 if (dle->create_index != 0) {
586 dbprintf(_("multiple index option\n"));
588 g_printf(_("ERROR [multiple index option]\n"));
591 dle->create_index = 1;
593 else if (BSTRNCMP(tok, "exclude-optional") == 0) {
594 if (dle->exclude_optional != 0) {
595 dbprintf(_("multiple exclude-optional option\n"));
597 g_printf(_("ERROR [multiple exclude-optional option]\n"));
600 dle->exclude_optional = 1;
602 else if (strcmp(tok, "include-optional") == 0) {
603 if (dle->include_optional != 0) {
604 dbprintf(_("multiple include-optional option\n"));
606 g_printf(_("ERROR [multiple include-optional option]\n"));
609 dle->include_optional = 1;
611 else if (BSTRNCMP(tok,"exclude-file=") == 0) {
612 exc = unquote_string(&tok[13]);
613 dle->exclude_file = append_sl(dle->exclude_file, exc);
616 else if (BSTRNCMP(tok,"exclude-list=") == 0) {
617 exc = unquote_string(&tok[13]);
618 dle->exclude_list = append_sl(dle->exclude_list, exc);
621 else if (BSTRNCMP(tok,"include-file=") == 0) {
622 inc = unquote_string(&tok[13]);
623 dle->include_file = append_sl(dle->include_file, inc);
626 else if (BSTRNCMP(tok,"include-list=") == 0) {
627 inc = unquote_string(&tok[13]);
628 dle->include_list = append_sl(dle->include_list, inc);
631 else if (BSTRNCMP(tok,"kencrypt") == 0) {
634 else if (strcmp(tok,"|") != 0) {
635 quoted = quote_string(tok);
636 dbprintf(_("unknown option %s\n"), quoted);
638 g_printf(_("ERROR [unknown option: %s]\n"), quoted);
642 tok = strtok(NULL, ";");
648 application_property_add_to_argv(
651 backup_support_option_t *bsu,
652 am_feature_t *amfeatures)
657 if (bsu->include_file && dle->include_file) {
658 for (incl = dle->include_file->first; incl != NULL;
660 g_ptr_array_add(argv_ptr, stralloc("--include-file"));
661 g_ptr_array_add(argv_ptr, stralloc(incl->name));
664 if (bsu->include_list && dle->include_list) {
665 for (incl = dle->include_list->first; incl != NULL;
667 g_ptr_array_add(argv_ptr, stralloc("--include-list"));
668 g_ptr_array_add(argv_ptr, stralloc(incl->name));
671 if (bsu->include_optional && dle->include_optional) {
672 g_ptr_array_add(argv_ptr, stralloc("--include-optional"));
673 g_ptr_array_add(argv_ptr, stralloc("yes"));
676 if (bsu->exclude_file && dle->exclude_file) {
677 for (excl = dle->exclude_file->first; excl != NULL;
679 g_ptr_array_add(argv_ptr, stralloc("--exclude-file"));
680 g_ptr_array_add(argv_ptr, stralloc(excl->name));
683 if (bsu->exclude_list && dle->exclude_list) {
684 for (excl = dle->exclude_list->first; excl != NULL;
686 g_ptr_array_add(argv_ptr, stralloc("--exclude-list"));
687 g_ptr_array_add(argv_ptr, stralloc(excl->name));
690 if (bsu->exclude_optional && dle->exclude_optional) {
691 g_ptr_array_add(argv_ptr, stralloc("--exclude-optional"));
692 g_ptr_array_add(argv_ptr, stralloc("yes"));
695 if (bsu->features && amfeatures) {
696 char *feature_string = am_feature_to_string(amfeatures);
697 g_ptr_array_add(argv_ptr, stralloc("--amfeatures"));
698 g_ptr_array_add(argv_ptr, feature_string);
701 if (dle->data_path == DATA_PATH_DIRECTTCP &&
702 bsu->data_path_set & DATA_PATH_DIRECTTCP) {
705 g_ptr_array_add(argv_ptr, stralloc("--data-path"));
706 g_ptr_array_add(argv_ptr, stralloc("directtcp"));
707 for (directtcp = dle->directtcp_list; directtcp != NULL;
708 directtcp = directtcp->next) {
709 g_ptr_array_add(argv_ptr, stralloc("--direct-tcp"));
710 g_ptr_array_add(argv_ptr, stralloc(directtcp->data));
711 break; /* XXX temporary; apps only support one ip:port pair */
716 property_add_to_argv(argv_ptr, dle->application_property);
720 backup_support_option_t *
721 backup_support_option(
723 g_option_t *g_options,
726 GPtrArray **errarray)
729 int supportin, supportout, supporterr;
731 GPtrArray *argv_ptr = g_ptr_array_new();
737 backup_support_option_t *bsu;
739 *errarray = g_ptr_array_new();
740 cmd = vstralloc(APPLICATION_DIR, "/", program, NULL);
741 g_ptr_array_add(argv_ptr, stralloc(program));
742 g_ptr_array_add(argv_ptr, stralloc("support"));
743 if (g_options->config) {
744 g_ptr_array_add(argv_ptr, stralloc("--config"));
745 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
747 if (g_options->hostname) {
748 g_ptr_array_add(argv_ptr, stralloc("--host"));
749 g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
752 g_ptr_array_add(argv_ptr, stralloc("--disk"));
753 g_ptr_array_add(argv_ptr, stralloc(disk));
756 g_ptr_array_add(argv_ptr, stralloc("--device"));
757 g_ptr_array_add(argv_ptr, stralloc(amdevice));
759 g_ptr_array_add(argv_ptr, NULL);
761 supporterr = fileno(stderr);
762 supportpid = pipespawnv(cmd, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 0,
763 &supportin, &supportout, &supporterr,
764 (char **)argv_ptr->pdata);
768 bsu = g_new0(backup_support_option_t, 1);
772 streamout = fdopen(supportout, "r");
774 error(_("Error opening pipe to child: %s"), strerror(errno));
777 while((line = agets(streamout)) != NULL) {
778 dbprintf(_("support line: %s\n"), line);
779 if (strncmp(line,"CONFIG ", 7) == 0) {
780 if (strcmp(line+7, "YES") == 0)
782 } else if (strncmp(line,"HOST ", 5) == 0) {
783 if (strcmp(line+5, "YES") == 0)
785 } else if (strncmp(line,"DISK ", 5) == 0) {
786 if (strcmp(line+5, "YES") == 0)
788 } else if (strncmp(line,"INDEX-LINE ", 11) == 0) {
789 if (strcmp(line+11, "YES") == 0)
791 } else if (strncmp(line,"INDEX-XML ", 10) == 0) {
792 if (strcmp(line+10, "YES") == 0)
794 } else if (strncmp(line,"MESSAGE-LINE ", 13) == 0) {
795 if (strcmp(line+13, "YES") == 0)
796 bsu->message_line = 1;
797 } else if (strncmp(line,"MESSAGE-XML ", 12) == 0) {
798 if (strcmp(line+12, "YES") == 0)
799 bsu->message_xml = 1;
800 } else if (strncmp(line,"RECORD ", 7) == 0) {
801 if (strcmp(line+7, "YES") == 0)
803 } else if (strncmp(line,"INCLUDE-FILE ", 13) == 0) {
804 if (strcmp(line+13, "YES") == 0)
805 bsu->include_file = 1;
806 } else if (strncmp(line,"INCLUDE-LIST ", 13) == 0) {
807 if (strcmp(line+13, "YES") == 0)
808 bsu->include_list = 1;
809 } else if (strncmp(line,"INCLUDE-LIST-GLOB ", 17) == 0) {
810 if (strcmp(line+17, "YES") == 0)
811 bsu->include_list_glob = 1;
812 } else if (strncmp(line,"INCLUDE-OPTIONAL ", 17) == 0) {
813 if (strcmp(line+17, "YES") == 0)
814 bsu->include_optional = 1;
815 } else if (strncmp(line,"EXCLUDE-FILE ", 13) == 0) {
816 if (strcmp(line+13, "YES") == 0)
817 bsu->exclude_file = 1;
818 } else if (strncmp(line,"EXCLUDE-LIST ", 13) == 0) {
819 if (strcmp(line+13, "YES") == 0)
820 bsu->exclude_list = 1;
821 } else if (strncmp(line,"EXCLUDE-LIST-GLOB ", 17) == 0) {
822 if (strcmp(line+17, "YES") == 0)
823 bsu->exclude_list_glob = 1;
824 } else if (strncmp(line,"EXCLUDE-OPTIONAL ", 17) == 0) {
825 if (strcmp(line+17, "YES") == 0)
826 bsu->exclude_optional = 1;
827 } else if (strncmp(line,"COLLECTION ", 11) == 0) {
828 if (strcmp(line+11, "YES") == 0)
830 } else if (strncmp(line,"CALCSIZE ", 9) == 0) {
831 if (strcmp(line+9, "YES") == 0)
833 } else if (strncmp(line,"CLIENT-ESTIMATE ", 16) == 0) {
834 if (strcmp(line+16, "YES") == 0)
835 bsu->client_estimate = 1;
836 } else if (strncmp(line,"MULTI-ESTIMATE ", 15) == 0) {
837 if (strcmp(line+15, "YES") == 0)
838 bsu->multi_estimate = 1;
839 } else if (strncmp(line,"MAX-LEVEL ", 10) == 0) {
840 bsu->max_level = atoi(line+10);
841 } else if (strncmp(line,"RECOVER-MODE ", 13) == 0) {
842 if (strcasecmp(line+13, "SMB") == 0)
843 bsu->smb_recover_mode = 1;
844 } else if (strncmp(line,"DATA-PATH ", 10) == 0) {
845 if (strcasecmp(line+10, "AMANDA") == 0)
846 bsu->data_path_set |= DATA_PATH_AMANDA;
847 else if (strcasecmp(line+10, "DIRECTTCP") == 0)
848 bsu->data_path_set |= DATA_PATH_DIRECTTCP;
849 } else if (strncmp(line,"RECOVER-PATH ", 13) == 0) {
850 if (strcasecmp(line+13, "CWD") == 0)
851 bsu->recover_path = RECOVER_PATH_CWD;
852 else if (strcasecmp(line+13, "REMOTE") == 0)
853 bsu->recover_path = RECOVER_PATH_REMOTE;
854 } else if (strncmp(line,"AMFEATURES ", 11) == 0) {
855 if (strcmp(line+11, "YES") == 0)
858 dbprintf(_("Invalid support line: %s\n"), line);
864 if (bsu->data_path_set == 0)
865 bsu->data_path_set = DATA_PATH_AMANDA;
867 streamerr = fdopen(supporterr, "r");
869 error(_("Error opening pipe to child: %s"), strerror(errno));
872 while((line = agets(streamerr)) != NULL) {
873 if (strlen(line) > 0) {
874 g_ptr_array_add(*errarray, line);
875 dbprintf("Application '%s': %s\n", program, line);
881 if (waitpid(supportpid, &status, 0) < 0) {
882 err = vstrallocf(_("waitpid failed: %s"), strerror(errno));
883 } else if (!WIFEXITED(status)) {
884 err = vstrallocf(_("exited with signal %d"), WTERMSIG(status));
885 } else if (WEXITSTATUS(status) != 0) {
886 err = vstrallocf(_("exited with status %d"), WEXITSTATUS(status));
890 g_ptr_array_add(*errarray, err);
891 dbprintf("Application '%s': %s\n", program, err);
894 g_ptr_array_free_full(argv_ptr);
902 execute_on_t execute_on,
903 g_option_t *g_options,
907 int scriptin, scriptout, scripterr;
909 GPtrArray *argv_ptr = g_ptr_array_new();
913 amwait_t wait_status;
914 char *command = NULL;
916 if ((script->execute_on & execute_on) == 0)
918 if (script->execute_where != ES_CLIENT)
921 cmd = vstralloc(APPLICATION_DIR, "/", script->plugin, NULL);
922 g_ptr_array_add(argv_ptr, stralloc(script->plugin));
924 switch (execute_on) {
925 case EXECUTE_ON_PRE_DLE_AMCHECK:
926 command = "PRE-DLE-AMCHECK";
928 case EXECUTE_ON_PRE_HOST_AMCHECK:
929 command = "PRE-HOST-AMCHECK";
931 case EXECUTE_ON_POST_DLE_AMCHECK:
932 command = "POST-DLE-AMCHECK";
934 case EXECUTE_ON_POST_HOST_AMCHECK:
935 command = "POST-HOST-AMCHECK";
937 case EXECUTE_ON_PRE_DLE_ESTIMATE:
938 command = "PRE-DLE-ESTIMATE";
940 case EXECUTE_ON_PRE_HOST_ESTIMATE:
941 command = "PRE-HOST-ESTIMATE";
943 case EXECUTE_ON_POST_DLE_ESTIMATE:
944 command = "POST-DLE-ESTIMATE";
946 case EXECUTE_ON_POST_HOST_ESTIMATE:
947 command = "POST-HOST-ESTIMATE";
949 case EXECUTE_ON_PRE_DLE_BACKUP:
950 command = "PRE-DLE-BACKUP";
952 case EXECUTE_ON_PRE_HOST_BACKUP:
953 command = "PRE-HOST-BACKUP";
955 case EXECUTE_ON_POST_DLE_BACKUP:
956 command = "POST-DLE-BACKUP";
958 case EXECUTE_ON_POST_HOST_BACKUP:
959 command = "POST-HOST-BACKUP";
961 case EXECUTE_ON_PRE_RECOVER:
962 command = "PRE-RECOVER";
964 case EXECUTE_ON_POST_RECOVER:
965 command = "POST-RECOVER";
967 case EXECUTE_ON_PRE_LEVEL_RECOVER:
968 command = "PRE-LEVEL-RECOVER";
970 case EXECUTE_ON_POST_LEVEL_RECOVER:
971 command = "POST-LEVEL-RECOVER";
973 case EXECUTE_ON_INTER_LEVEL_RECOVER:
974 command = "INTER-LEVEL-RECOVER";
977 g_ptr_array_add(argv_ptr, stralloc(command));
978 g_ptr_array_add(argv_ptr, stralloc("--execute-where"));
979 g_ptr_array_add(argv_ptr, stralloc("client"));
981 if (g_options->config) {
982 g_ptr_array_add(argv_ptr, stralloc("--config"));
983 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
985 if (g_options->hostname) {
986 g_ptr_array_add(argv_ptr, stralloc("--host"));
987 g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
990 g_ptr_array_add(argv_ptr, stralloc("--disk"));
991 g_ptr_array_add(argv_ptr, stralloc(dle->disk));
994 g_ptr_array_add(argv_ptr, stralloc("--device"));
995 g_ptr_array_add(argv_ptr, stralloc(dle->device));
997 if (dle->levellist) {
998 levellist_t levellist;
999 char number[NUM_STR_SIZE];
1000 for (levellist=dle->levellist; levellist; levellist=levellist->next) {
1001 level_t *alevel = (level_t *)levellist->data;
1002 g_ptr_array_add(argv_ptr, stralloc("--level"));
1003 g_snprintf(number, SIZEOF(number), "%d", alevel->level);
1004 g_ptr_array_add(argv_ptr, stralloc(number));
1007 property_add_to_argv(argv_ptr, script->property);
1008 g_ptr_array_add(argv_ptr, NULL);
1010 scriptpid = pipespawnv(cmd, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 0,
1011 &scriptin, &scriptout, &scripterr,
1012 (char **)argv_ptr->pdata);
1016 script->result = g_new0(client_script_result_t, 1);
1017 script->result->proplist =
1018 g_hash_table_new_full(g_str_hash, g_str_equal,
1019 &g_free, &destroy_slist_free_full);
1020 script->result->output = g_ptr_array_new();
1021 script->result->err = g_ptr_array_new();
1023 streamout = fdopen(scriptout, "r");
1025 while((line = agets(streamout)) != NULL) {
1026 dbprintf("script: %s\n", line);
1027 if (BSTRNCMP(line, "PROPERTY ") == 0) {
1028 char *property_name, *property_value;
1029 property_name = line + 9;
1030 property_value = strchr(property_name,' ');
1031 if (property_value == NULL) {
1032 char *msg = g_strdup_printf(
1033 "ERROR %s: Bad output property: %s",
1034 script->plugin, line);
1035 g_ptr_array_add(script->result->output, msg);
1037 property_t *property;
1039 *property_value++ = '\0';
1040 property_name = stralloc(property_name);
1041 property_value = stralloc(property_value);
1042 property = g_hash_table_lookup(script->result->proplist,
1045 property = g_new0(property_t, 1);
1046 g_hash_table_insert(script->result->proplist,
1047 property_name, property);
1049 property->values = g_slist_append(property->values,
1054 g_ptr_array_add(script->result->output, line);
1060 streamerr = fdopen(scripterr, "r");
1062 while((line = agets(streamerr)) != NULL) {
1063 g_ptr_array_add(script->result->err,
1064 g_strdup_printf(_("Script '%s' command '%s': %s"),
1065 script->plugin, command, line));
1070 waitpid(scriptpid, &wait_status, 0);
1071 if (WIFSIGNALED(wait_status)) {
1072 g_ptr_array_add(script->result->err,
1073 g_strdup_printf(_("Script '%s' command '%s' terminated with signal %d: see %s"),
1074 script->plugin, command,
1075 WTERMSIG(wait_status),
1077 } else if (WIFEXITED(wait_status)) {
1078 if (WEXITSTATUS(wait_status) != 0) {
1079 g_ptr_array_add(script->result->err,
1080 g_strdup_printf(_("Script '%s' command '%s' exited with status %d: see %s"),
1081 script->plugin, command,
1082 WEXITSTATUS(wait_status),
1089 g_ptr_array_free_full(argv_ptr);
1092 void run_client_script_output(gpointer data, gpointer user_data);
1093 void run_client_script_err_amcheck(gpointer data, gpointer user_data);
1094 void run_client_script_err_estimate(gpointer data, gpointer user_data);
1095 void run_client_script_err_backup(gpointer data, gpointer user_data);
1096 void run_client_script_err_recover(gpointer data, gpointer user_data);
1098 typedef struct script_output_s {
1104 run_client_script_output(
1109 script_output_t *so = user_data;
1111 if (line && so->stream) {
1112 g_fprintf(so->stream, "%s\n", line);
1117 run_client_script_err_amcheck(
1122 script_output_t *so = user_data;
1124 if (line && so->stream) {
1125 g_fprintf(so->stream, "ERROR %s\n", line);
1130 run_client_script_err_estimate(
1135 script_output_t *so = user_data;
1137 if (line && so->stream) {
1138 char *qdisk = quote_string(so->dle->disk);
1139 g_fprintf(so->stream, "%s 0 WARNING \"%s\"\n", qdisk, line);
1145 run_client_script_err_backup(
1150 script_output_t *so = user_data;
1152 if (line && so->stream) {
1153 g_fprintf(so->stream, "? %s\n", line);
1158 run_client_script_err_recover(
1163 script_output_t *so = user_data;
1165 if (line && so->stream) {
1166 g_fprintf(so->stream, "%s\n", line);
1172 execute_on_t execute_on,
1173 g_option_t *g_options,
1179 GFunc client_script_err = NULL;
1180 script_output_t so = { streamout, dle };
1182 for (scriptlist = dle->scriptlist; scriptlist != NULL;
1183 scriptlist = scriptlist->next) {
1184 script = (script_t *)scriptlist->data;
1185 run_client_script(script, execute_on, g_options, dle);
1186 if (script->result && script->result->output) {
1187 g_ptr_array_foreach(script->result->output,
1188 run_client_script_output,
1190 g_ptr_array_free(script->result->output, TRUE);
1191 script->result->output = NULL;
1193 if (script->result && script->result->err) {
1194 switch (execute_on) {
1195 case EXECUTE_ON_PRE_DLE_AMCHECK:
1196 case EXECUTE_ON_PRE_HOST_AMCHECK:
1197 case EXECUTE_ON_POST_DLE_AMCHECK:
1198 case EXECUTE_ON_POST_HOST_AMCHECK:
1199 client_script_err = run_client_script_err_amcheck;
1201 case EXECUTE_ON_PRE_DLE_ESTIMATE:
1202 case EXECUTE_ON_PRE_HOST_ESTIMATE:
1203 case EXECUTE_ON_POST_DLE_ESTIMATE:
1204 case EXECUTE_ON_POST_HOST_ESTIMATE:
1205 if (am_has_feature(g_options->features,
1206 fe_sendsize_rep_warning)) {
1207 client_script_err = run_client_script_err_estimate;
1210 case EXECUTE_ON_PRE_DLE_BACKUP:
1211 case EXECUTE_ON_PRE_HOST_BACKUP:
1212 case EXECUTE_ON_POST_DLE_BACKUP:
1213 case EXECUTE_ON_POST_HOST_BACKUP:
1214 client_script_err = run_client_script_err_backup;
1216 case EXECUTE_ON_PRE_RECOVER:
1217 case EXECUTE_ON_POST_RECOVER:
1218 case EXECUTE_ON_PRE_LEVEL_RECOVER:
1219 case EXECUTE_ON_POST_LEVEL_RECOVER:
1220 case EXECUTE_ON_INTER_LEVEL_RECOVER:
1221 client_script_err = run_client_script_err_recover;
1223 if (client_script_err != NULL) {
1224 g_ptr_array_foreach(script->result->err,
1228 g_ptr_array_free(script->result->err, TRUE);
1229 script->result->err = NULL;
1245 char *cmd, *cmdline;
1247 GPtrArray *argv_ptr = g_ptr_array_new();
1248 char tmppath[PATH_MAX];
1249 char number[NUM_STR_SIZE];
1254 int pipefd = -1, nullfd = -1;
1257 FILE *dumpout = NULL;
1259 char *errmsg = NULL;
1260 off_t size = (off_t)1;
1262 amwait_t wait_status;
1266 char *amandates_file;
1268 qdisk = quote_string(disk);
1270 amandates_file = getconf_str(CNF_AMANDATES);
1271 if(!start_amandates(amandates_file, 0)) {
1272 char *errstr = strerror(errno);
1273 char *errmsg = vstrallocf(_("could not open %s: %s"), amandates_file, errstr);
1274 char *qerrmsg = quote_string(errmsg);
1275 g_printf(_("ERROR %s\n"), qerrmsg);
1283 cmd = vstralloc(amlibexecdir, "/", "calcsize", NULL);
1286 g_ptr_array_add(argv_ptr, stralloc("calcsize"));
1288 g_ptr_array_add(argv_ptr, stralloc(config));
1290 g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
1292 g_ptr_array_add(argv_ptr, stralloc(program));
1294 canonicalize_pathname(disk, tmppath);
1295 g_ptr_array_add(argv_ptr, stralloc(tmppath));
1296 canonicalize_pathname(dirname, tmppath);
1297 g_ptr_array_add(argv_ptr, stralloc(tmppath));
1300 g_ptr_array_add(argv_ptr, stralloc("-X"));
1301 g_ptr_array_add(argv_ptr, stralloc(file_exclude));
1305 g_ptr_array_add(argv_ptr, stralloc("-I"));
1306 g_ptr_array_add(argv_ptr, stralloc(file_include));
1309 for (alevel = levels; alevel != NULL; alevel = alevel->next) {
1310 amdp = amandates_lookup(disk);
1311 level = GPOINTER_TO_INT(alevel->data);
1312 dbprintf("level: %d\n", level);
1314 for (i=0; i < level; i++) {
1315 if (dumpsince < amdp->dates[i])
1316 dumpsince = amdp->dates[i];
1318 g_snprintf(number, SIZEOF(number), "%d", level);
1319 g_ptr_array_add(argv_ptr, stralloc(number));
1320 g_snprintf(number, SIZEOF(number), "%d", dumpsince);
1321 g_ptr_array_add(argv_ptr, stralloc(number));
1324 g_ptr_array_add(argv_ptr, NULL);
1325 command = (char *)g_ptr_array_index(argv_ptr, 0);
1326 cmdline = stralloc(command);
1327 for(i = 1; i < argv_ptr->len - 1; i++)
1328 cmdline = vstrextend(&cmdline, " ",
1329 (char *)g_ptr_array_index(argv_ptr,i), NULL);
1330 dbprintf(_("running: \"%s\"\n"), cmdline);
1333 start_time = curclock();
1335 fflush(stderr); fflush(stdout);
1337 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1338 errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
1340 dbprintf("%s\n", errmsg);
1344 calcpid = pipespawnv(cmd, STDERR_PIPE, 0,
1345 &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
1348 dumpout = fdopen(pipefd,"r");
1350 error(_("Can't fdopen: %s"), strerror(errno));
1354 match_expr = vstralloc(" %d SIZE %lld", NULL);
1355 len = strlen(qdisk);
1356 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1357 long long size_ = (long long)0;
1358 if (line[0] == '\0' || (int)strlen(line) <= len)
1360 /* Don't use sscanf for qdisk because it can have a '%'. */
1361 if (strncmp(line, qdisk, len) == 0 &&
1362 sscanf(line+len, match_expr, &level, &size_) == 2) {
1363 g_printf("%d %lld %d\n", level, size_, 1); /* write to sendsize */
1364 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1365 qdisk, level, size_);
1367 size = (off_t)size_;
1371 dbprintf(_("waiting for %s %s child (pid=%d)\n"),
1372 command, qdisk, (int)calcpid);
1373 waitpid(calcpid, &wait_status, 0);
1374 if (WIFSIGNALED(wait_status)) {
1375 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1376 "calcsize", WTERMSIG(wait_status),
1378 } else if (WIFEXITED(wait_status)) {
1379 if (WEXITSTATUS(wait_status) != 0) {
1380 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1381 "calcsize", WEXITSTATUS(wait_status),
1387 errmsg = vstrallocf(_("%s got bad exit: see %s"),
1388 "calcsize", dbfn());
1391 dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
1393 (int)calcpid, WEXITSTATUS(wait_status));
1395 dbprintf(_(".....\n"));
1396 dbprintf(_("estimate time for %s: %s\n"),
1398 walltime_str(timessub(curclock(), start_time)));
1401 if (errmsg && errmsg[0] != '\0') {
1402 char *qerrmsg = quote_string(errmsg);
1403 dbprintf(_("errmsg is %s\n"), errmsg);
1404 g_printf("ERROR %s\n", qerrmsg);
1409 g_ptr_array_free_full(argv_ptr);
1420 char *noun, *adjective;
1421 char *quoted = quote_string(filename);
1424 noun = "find", adjective = "exists";
1425 else if((mode & X_OK) == X_OK)
1426 noun = "execute", adjective = "executable";
1427 else if((mode & (W_OK|R_OK)) == (W_OK|R_OK))
1428 noun = "read/write", adjective = "read/writable";
1430 noun = "access", adjective = "accessible";
1432 if(access(filename, mode) == -1)
1433 g_printf(_("ERROR [can not %s %s: %s]\n"), noun, quoted, strerror(errno));
1435 g_printf(_("OK %s %s\n"), quoted, adjective);
1444 struct stat stat_buf;
1447 if(!stat(filename, &stat_buf)) {
1448 if(!S_ISREG(stat_buf.st_mode)) {
1449 quoted = quote_string(filename);
1450 g_printf(_("ERROR [%s is not a file]\n"), quoted);
1454 int save_errno = errno;
1455 quoted = quote_string(filename);
1456 g_printf(_("ERROR [can not stat %s: %s]\n"), quoted,
1457 strerror(save_errno));
1460 if (getuid() == geteuid()) {
1461 check_access(filename, mode);
1470 struct stat stat_buf;
1474 if(!stat(dirname, &stat_buf)) {
1475 if(!S_ISDIR(stat_buf.st_mode)) {
1476 quoted = quote_string(dirname);
1477 g_printf(_("ERROR [%s is not a directory]\n"), quoted);
1481 int save_errno = errno;
1482 quoted = quote_string(dirname);
1483 g_printf(_("ERROR [can not stat %s: %s]\n"), quoted,
1484 strerror(save_errno));
1487 if (getuid() == geteuid()) {
1488 dir = stralloc2(dirname, "/.");
1489 check_access(dir, mode);
1498 #ifndef SINGLE_USERID
1499 struct stat stat_buf;
1500 char *quoted = quote_string(filename);
1502 if(!stat(filename, &stat_buf)) {
1503 if(stat_buf.st_uid != 0 ) {
1504 g_printf(_("ERROR [%s is not owned by root]\n"), quoted);
1506 if((stat_buf.st_mode & S_ISUID) != S_ISUID) {
1507 g_printf(_("ERROR [%s is not SUID root]\n"), quoted);
1511 g_printf(_("ERROR [can not stat %s: %s]\n"), quoted, strerror(errno));
1515 (void)filename; /* Quiet unused parameter warning */
1520 * Returns the value of the first integer in a string.
1534 while(ch && !isdigit(ch)) ch = *str++;
1535 if (pos == 1) break;
1537 while(ch && (isdigit(ch) || ch == '.')) ch = *str++;
1540 while(isdigit(ch) || ch == '.') ch = *str++;
1549 config_errors_to_error_string(
1553 gboolean multiple_errors = FALSE;
1556 errmsg = (char *)errlist->data;
1558 multiple_errors = TRUE;
1560 errmsg = _("(no error message)");
1563 return vstrallocf("ERROR %s%s", errmsg,
1564 multiple_errors? _(" (additional errors not displayed)"):"");
1571 amregex_t **re_table,
1572 amregex_t *orig_re_table,
1573 GSList *normal_message,
1574 GSList *ignore_message,
1575 GSList *strange_message)
1579 for(rp = orig_re_table; rp->regex != NULL; rp++) {
1580 if (rp->typ == typ) {
1584 for (mes = normal_message; mes != NULL; mes = mes->next) {
1585 if (strcmp(rp->regex, (char *)mes->data) == 0)
1588 for (mes = ignore_message; mes != NULL; mes = mes->next) {
1589 if (strcmp(rp->regex, (char *)mes->data) == 0)
1592 for (mes = strange_message; mes != NULL; mes = mes->next) {
1593 if (strcmp(rp->regex, (char *)mes->data) == 0)
1597 (*re_table)->regex = rp->regex;
1598 (*re_table)->srcline = rp->srcline;
1599 (*re_table)->scale = rp->scale;
1600 (*re_table)->field = rp->field;
1601 (*re_table)->typ = rp->typ;
1611 amregex_t **re_table,
1616 for (mes = message; mes != NULL; mes = mes->next) {
1617 (*re_table)->regex = (char *)mes->data;
1618 (*re_table)->srcline = 0;
1619 (*re_table)->scale = 0;
1620 (*re_table)->field = 0;
1621 (*re_table)->typ = typ;
1628 amregex_t *orig_re_table,
1629 GSList *normal_message,
1630 GSList *ignore_message,
1631 GSList *strange_message)
1635 amregex_t *re_table, *new_re_table;
1637 for(rp = orig_re_table; rp->regex != NULL; rp++) {
1640 nb += g_slist_length(normal_message);
1641 nb += g_slist_length(ignore_message);
1642 nb += g_slist_length(strange_message);
1645 re_table = new_re_table = malloc(nb * sizeof(amregex_t));
1647 /* add SIZE from orig_re_table */
1648 add_type_table(DMP_SIZE, &re_table, orig_re_table,
1649 normal_message, ignore_message, strange_message);
1651 /* add ignore_message */
1652 add_list_table(DMP_IGNORE, &re_table, ignore_message);
1654 /* add IGNORE from orig_re_table */
1655 add_type_table(DMP_IGNORE, &re_table, orig_re_table,
1656 normal_message, ignore_message, strange_message);
1658 /* add normal_message */
1659 add_list_table(DMP_NORMAL, &re_table, normal_message);
1661 /* add NORMAL from orig_re_table */
1662 add_type_table(DMP_NORMAL, &re_table, orig_re_table,
1663 normal_message, ignore_message, strange_message);
1665 /* add strange_message */
1666 add_list_table(DMP_STRANGE, &re_table, strange_message);
1668 /* add STRANGE from orig_re_table */
1669 add_type_table(DMP_STRANGE, &re_table, orig_re_table,
1670 normal_message, ignore_message, strange_message);
1672 /* Add DMP_STRANGE with NULL regex, */
1673 /* it is not copied by previous statement */
1674 re_table->regex = NULL;
1675 re_table->srcline = 0;
1676 re_table->scale = 0;
1677 re_table->field = 0;
1678 re_table->typ = DMP_STRANGE;
1680 return new_re_table;
1691 gpointer user_data_p)
1693 char *property_s = key_p;
1694 GSList *value_s = value_p;
1695 merge_property_t *merge_p = user_data_p;
1696 GSList *value = g_hash_table_lookup(merge_p->result, property_s);
1698 if (value) { /* remove old value */
1699 g_hash_table_remove(merge_p->result, key_p);
1701 g_hash_table_insert(merge_p->result, key_p, value_s);
1706 proplist_t proplist1,
1707 proplist_t proplist2)
1709 merge_property_t merge_p = {proplist1};
1711 if (proplist2 == NULL) {
1714 g_hash_table_foreach(proplist2,