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: sendbackup-gnutar.c,v 1.56.2.15.4.4.2.11 2003/10/30 18:04:45 martinea Exp $
29 * send backup data using GNU tar
33 #include "sendbackup.h"
34 #include "amandates.h"
37 #include "getfsent.h" /* for amname_to_dirname lookup */
45 #include "sendbackup-krb4.h"
46 #else /* I'd tell you what this does */
47 #define NAUGHTY_BITS /* but then I'd have to kill you */
51 static regex_t re_table[] = {
52 /* tar prints the size in bytes */
53 AM_SIZE_RE("^ *Total bytes written: [0-9][0-9]*", 1),
55 AM_NORMAL_RE("^Elapsed time:"),
56 AM_NORMAL_RE("^Throughput"),
58 /* GNU tar 1.13.17 will print this warning when (not) backing up a
60 AM_NORMAL_RE(": socket ignored$"),
62 /* GNUTAR produces a few error messages when files are modified or
63 removed while it is running. They may cause data to be lost, but
64 then they may not. We shouldn't consider them NORMAL until
65 further investigation. */
66 #ifdef IGNORE_TAR_ERRORS
67 AM_NORMAL_RE(": File .* shrunk by [0-9][0-9]* bytes, padding with zeros"),
68 AM_NORMAL_RE(": Cannot add file .*: No such file or directory$"),
69 AM_NORMAL_RE(": Error exit delayed from previous errors"),
72 /* samba may produce these output messages */
73 AM_NORMAL_RE("^[Aa]dded interface"),
74 AM_NORMAL_RE("^session request to "),
75 AM_NORMAL_RE("^tar: dumped [0-9][0-9]* (tar )?files"),
78 AM_NORMAL_RE("^doing parameter"),
79 AM_NORMAL_RE("^pm_process\\(\\)"),
80 AM_NORMAL_RE("^adding IPC"),
81 AM_NORMAL_RE("^Opening"),
82 AM_NORMAL_RE("^Connect"),
83 AM_NORMAL_RE("^Domain="),
85 AM_NORMAL_RE("^security="),
86 AM_NORMAL_RE("^capabilities"),
87 AM_NORMAL_RE("^Sec mode "),
88 AM_NORMAL_RE("^Got "),
89 AM_NORMAL_RE("^Chose protocol "),
90 AM_NORMAL_RE("^Server "),
91 AM_NORMAL_RE("^Timezone "),
92 AM_NORMAL_RE("^received"),
93 AM_NORMAL_RE("^FINDFIRST"),
94 AM_NORMAL_RE("^FINDNEXT"),
95 AM_NORMAL_RE("^dos_clean_name"),
96 AM_NORMAL_RE("^file"),
97 AM_NORMAL_RE("^getting file"),
98 AM_NORMAL_RE("^Rejected chained"),
99 AM_NORMAL_RE("^nread="),
100 AM_NORMAL_RE("^\\([0-9][0-9]* kb/s\\)"),
101 AM_NORMAL_RE("^\\([0-9][0-9]*\\.[0-9][0-9]* kb/s\\)"),
102 AM_NORMAL_RE("^[ \t]*[0-9][0-9]* \\([ \t]*[0-9][0-9]*\\.[0-9][0-9]* kb/s\\)"),
103 AM_NORMAL_RE("^[ \t]*directory "),
104 AM_NORMAL_RE("^load_client_codepage"),
107 #ifdef IGNORE_SMBCLIENT_ERRORS
108 /* This will cause amanda to ignore real errors, but that may be
109 * unavoidable when you're backing up system disks. It seems to be
110 * a safe thing to do if you know what you're doing. */
111 AM_NORMAL_RE("^ERRDOS - ERRbadshare opening remote file"),
112 AM_NORMAL_RE("^ERRDOS - ERRbadfile opening remote file"),
113 AM_NORMAL_RE("^ERRDOS - ERRnoaccess opening remote file"),
114 AM_NORMAL_RE("^ERRSRV - ERRaccess setting attributes on file"),
115 AM_NORMAL_RE("^ERRDOS - ERRnoaccess setting attributes on file"),
118 #if SAMBA_VERSION >= 2
119 /* Backup attempt of nonexisting directory */
120 AM_ERROR_RE("ERRDOS - ERRbadpath (Directory invalid.)"),
123 /* catch-all: DMP_STRANGE is returned for all other lines */
131 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
132 static char *incrname = NULL;
135 static void start_backup(host, disk, amdevice, level, dumpdate, dataf, mesgf, indexf)
137 char *disk, *amdevice;
138 int level, dataf, mesgf, indexf;
143 char *indexcmd = NULL;
144 char *dirname = NULL;
146 char dumptimestr[80];
148 amandates_t *amdates;
149 time_t prev_dumptime;
150 char *error_pn = NULL;
152 error_pn = stralloc2(get_pname(), "-smbclient");
154 fprintf(stderr, "%s: start [%s:%s level %d]\n",
155 get_pname(), host, disk, level);
159 if(options->compress == COMPR_FAST || options->compress == COMPR_BEST) {
160 char *compopt = skip_argument;
162 #if defined(COMPRESS_BEST_OPT) && defined(COMPRESS_FAST_OPT)
163 if(options->compress == COMPR_BEST) {
164 compopt = COMPRESS_BEST_OPT;
166 compopt = COMPRESS_FAST_OPT;
169 comppid = pipespawn(COMPRESS_PATH, STDIN_PIPE,
170 &dumpout, &dataf, &mesgf,
171 COMPRESS_PATH, compopt, NULL);
172 dbprintf(("%s: pid %ld: %s",
173 debug_prefix_time("-gnutar"), (long)comppid, COMPRESS_PATH));
174 if(compopt != skip_argument) {
175 dbprintf((" %s", compopt));
183 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR /* { */
184 #ifdef SAMBA_CLIENT /* { */
185 if (amdevice[0] == '/' && amdevice[1]=='/')
190 char *basename = NULL;
191 char number[NUM_STR_SIZE];
194 char *inputname = NULL;
200 basename = vstralloc(GNUTAR_LISTED_INCREMENTAL_DIR,
206 * The loop starts at the first character of the host name,
209 s = basename + sizeof(GNUTAR_LISTED_INCREMENTAL_DIR);
210 while((ch = *s++) != '\0') {
211 if(ch == '/' || isspace(ch)) s[-1] = '_';
214 ap_snprintf(number, sizeof(number), "%d", level);
215 incrname = vstralloc(basename, "_", number, ".new", NULL);
219 * Open the listed incremental file for the previous level. Search
220 * backward until one is found. If none are found (which will also
221 * be true for a level 0), arrange to read from /dev/null.
225 if (--baselevel >= 0) {
226 ap_snprintf(number, sizeof(number), "%d", baselevel);
227 inputname = newvstralloc(inputname,
228 basename, "_", number, NULL);
230 inputname = newstralloc(inputname, "/dev/null");
232 if ((in = fopen(inputname, "r")) == NULL) {
233 int save_errno = errno;
235 dbprintf(("%s: error opening %s: %s\n",
236 debug_prefix_time("-gnutar"),
238 strerror(save_errno)));
240 error("error [opening %s: %s]", inputname, strerror(save_errno));
246 * Copy the previous listed incremental file to the new one.
248 if ((out = fopen(incrname, "w")) == NULL) {
249 error("error [opening %s: %s]", incrname, strerror(errno));
252 for (; (line = agets(in)) != NULL; free(line)) {
253 if(fputs(line, out) == EOF || putc('\n', out) == EOF) {
254 error("error [writing to %s: %s]", incrname, strerror(errno));
260 error("error [reading from %s: %s]", inputname, strerror(errno));
262 if (fclose(in) == EOF) {
263 error("error [closing %s: %s]", inputname, strerror(errno));
266 if (fclose(out) == EOF) {
267 error("error [closing %s: %s]", incrname, strerror(errno));
271 dbprintf(("%s: doing level %d dump as listed-incremental",
272 debug_prefix_time("-gnutar"), level));
274 dbprintf((" from %s", inputname));
276 dbprintf((" to %s\n", incrname));
282 /* find previous dump time */
284 if(!start_amandates(0))
285 error("error [opening %s: %s]", AMANDATES_FILE, strerror(errno));
287 amdates = amandates_lookup(disk);
289 prev_dumptime = EPOCH;
290 for(l = 0; l < level; l++) {
291 if(amdates->dates[l] > prev_dumptime)
292 prev_dumptime = amdates->dates[l];
298 gmtm = gmtime(&prev_dumptime);
299 ap_snprintf(dumptimestr, sizeof(dumptimestr),
300 "%04d-%02d-%02d %2d:%02d:%02d GMT",
301 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
302 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
304 dbprintf(("%s: doing level %d dump from date: %s\n",
305 debug_prefix_time("-gnutar"), level, dumptimestr));
307 dirname = amname_to_dirname(amdevice);
309 cur_dumptime = time(0);
311 cur_disk = stralloc(disk);
312 indexcmd = vstralloc(
324 #ifdef SAMBA_CLIENT /* { */
325 /* Use sambatar if the disk to back up is a PC disk */
326 if (amdevice[0] == '/' && amdevice[1]=='/') {
327 char *sharename = NULL, *user_and_password = NULL, *domain = NULL;
328 char *share = NULL, *subdir = NULL;
336 parsesharename(amdevice, &share, &subdir);
342 error("cannot parse disk entry '%s' for share/subdir", disk);
344 if ((subdir) && (SAMBA_VERSION < 2)) {
349 error("subdirectory specified for share '%s' but samba not v2 or better", disk);
351 if ((user_and_password = findpass(share, &domain)) == NULL) {
353 memset(domain, '\0', strlen(domain));
358 error("error [invalid samba host or password not found?]");
360 lpass = strlen(user_and_password);
361 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
362 memset(user_and_password, '\0', lpass);
363 amfree(user_and_password);
365 memset(domain, '\0', strlen(domain));
370 error("password field not \'user%%pass\' for %s", disk);
373 pwtext_len = strlen(pwtext);
374 if ((sharename = makesharename(share, 0)) == 0) {
375 memset(user_and_password, '\0', lpass);
376 amfree(user_and_password);
378 memset(domain, '\0', strlen(domain));
383 error("error [can't make share name of %s]", share);
386 taropt = stralloc("-T");
387 if(options->exclude_file && options->exclude_file->nb_element == 1) {
388 strappend(taropt, "X");
390 #if SAMBA_VERSION >= 2
391 strappend(taropt, "q");
393 strappend(taropt, "c");
395 strappend(taropt, "g");
396 } else if (!options->no_record) {
397 strappend(taropt, "a");
400 dbprintf(("%s: backup of %s", debug_prefix_time("-gnutar"), sharename));
402 dbprintf(("/%s",subdir));
406 program->backup_name = program->restore_name = SAMBA_CLIENT;
407 cmd = stralloc(program->backup_name);
410 start_index(options->createindex, dumpout, mesgf, indexf, indexcmd);
412 if (pwtext_len > 0) {
413 pw_fd_env = "PASSWD_FD";
415 pw_fd_env = "dummy_PASSWD_FD";
417 dumppid = pipespawn(cmd, STDIN_PIPE|PASSWD_PIPE,
418 &dumpin, &dumpout, &mesgf,
422 *user_and_password ? "-U" : skip_argument,
423 *user_and_password ? user_and_password : skip_argument,
425 domain ? "-W" : skip_argument,
426 domain ? domain : skip_argument,
427 #if SAMBA_VERSION >= 2
428 subdir ? "-D" : skip_argument,
429 subdir ? subdir : skip_argument,
434 options->exclude_file && options->exclude_file->nb_element == 1 ? options->exclude_file->first->name : skip_argument,
437 memset(domain, '\0', strlen(domain));
440 if(pwtext_len > 0 && fullwrite(passwdf, pwtext, pwtext_len) < 0) {
441 int save_errno = errno;
444 memset(user_and_password, '\0', lpass);
445 amfree(user_and_password);
448 error("error [password write failed: %s]", strerror(save_errno));
450 memset(user_and_password, '\0', lpass);
451 amfree(user_and_password);
465 char *file_exclude = NULL;
466 char *file_include = NULL;
468 if(options->exclude_file) nb_exclude+=options->exclude_file->nb_element;
469 if(options->exclude_list) nb_exclude+=options->exclude_list->nb_element;
470 if(options->include_file) nb_include+=options->include_file->nb_element;
471 if(options->include_list) nb_include+=options->include_list->nb_element;
473 if(nb_exclude > 0) file_exclude = build_exclude(disk, amdevice, options, 0);
474 if(nb_include > 0) file_include = build_include(disk, amdevice, options, 0);
476 my_argv = alloc(sizeof(char *) * (17 + (nb_exclude*2)+(nb_include*2)));
478 cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
481 start_index(options->createindex, dumpout, mesgf, indexf, indexcmd);
483 my_argv[i++] = "gtar";
484 my_argv[i++] = "--create";
485 my_argv[i++] = "--file";
487 my_argv[i++] = "--directory";
488 my_argv[i++] = dirname;
489 my_argv[i++] = "--one-file-system";
490 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
491 my_argv[i++] = "--listed-incremental";
492 my_argv[i++] = incrname;
494 my_argv[i++] = "--incremental";
495 my_argv[i++] = "--newer";
496 my_argv[i++] = dumptimestr;
498 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
499 /* --atime-preserve causes gnutar to call
500 * utime() after reading files in order to
501 * adjust their atime. However, utime()
502 * updates the file's ctime, so incremental
503 * dumps will think the file has changed. */
504 my_argv[i++] = "--atime-preserve";
506 my_argv[i++] = "--sparse";
507 my_argv[i++] = "--ignore-failed-read";
508 my_argv[i++] = "--totals";
511 my_argv[i++] = "--exclude-from";
512 my_argv[i++] = file_exclude;
516 my_argv[i++] = "--files-from";
517 my_argv[i++] = file_include;
523 dumppid = pipespawnv(cmd, STDIN_PIPE,
524 &dumpin, &dumpout, &mesgf, my_argv);
526 amfree(file_exclude);
527 amfree(file_include);
530 dbprintf(("%s: %s: pid %ld\n",
531 debug_prefix_time("-gnutar"),
540 /* close the write ends of the pipes */
546 if (options->createindex)
550 static void end_backup(goterror)
553 if(!options->no_record && !goterror) {
554 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
555 if (incrname != NULL && strlen(incrname) > 4) {
558 nodotnew = stralloc(incrname);
559 nodotnew[strlen(nodotnew)-4] = '\0';
560 if (rename(incrname, nodotnew) != 0) {
561 error("error [renaming %s to %s: %s]",
562 incrname, nodotnew, strerror(errno));
569 if(!start_amandates(1))
570 error("error [opening %s: %s]", AMANDATES_FILE, strerror(errno));
571 amandates_updateone(cur_disk, cur_level, cur_dumptime);
577 backup_program_t gnutar_program = {
584 re_table, start_backup, end_backup