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.2.2 2005/06/23 08:46:07 weichinger 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.)"),
121 AM_NORMAL_RE("^Domain="),
124 /* catch-all: DMP_STRANGE is returned for all other lines */
132 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
133 static char *incrname = NULL;
136 static void start_backup(host, disk, amdevice, level, dumpdate, dataf, mesgf, indexf)
138 char *disk, *amdevice;
139 int level, dataf, mesgf, indexf;
144 char *indexcmd = NULL;
145 char *dirname = NULL;
147 char dumptimestr[80];
149 amandates_t *amdates;
150 time_t prev_dumptime;
151 char *error_pn = NULL;
153 error_pn = stralloc2(get_pname(), "-smbclient");
155 fprintf(stderr, "%s: start [%s:%s level %d]\n",
156 get_pname(), host, disk, level);
160 if(options->compress == COMPR_FAST || options->compress == COMPR_BEST) {
161 char *compopt = skip_argument;
163 #if defined(COMPRESS_BEST_OPT) && defined(COMPRESS_FAST_OPT)
164 if(options->compress == COMPR_BEST) {
165 compopt = COMPRESS_BEST_OPT;
167 compopt = COMPRESS_FAST_OPT;
170 comppid = pipespawn(COMPRESS_PATH, STDIN_PIPE,
171 &dumpout, &dataf, &mesgf,
172 COMPRESS_PATH, compopt, NULL);
173 dbprintf(("%s: pid %ld: %s",
174 debug_prefix_time("-gnutar"), (long)comppid, COMPRESS_PATH));
175 if(compopt != skip_argument) {
176 dbprintf((" %s", compopt));
184 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR /* { */
185 #ifdef SAMBA_CLIENT /* { */
186 if (amdevice[0] == '/' && amdevice[1]=='/')
191 char *basename = NULL;
192 char number[NUM_STR_SIZE];
195 char *inputname = NULL;
201 basename = vstralloc(GNUTAR_LISTED_INCREMENTAL_DIR,
207 * The loop starts at the first character of the host name,
210 s = basename + sizeof(GNUTAR_LISTED_INCREMENTAL_DIR);
211 while((ch = *s++) != '\0') {
212 if(ch == '/' || isspace(ch)) s[-1] = '_';
215 ap_snprintf(number, sizeof(number), "%d", level);
216 incrname = vstralloc(basename, "_", number, ".new", NULL);
220 * Open the listed incremental file for the previous level. Search
221 * backward until one is found. If none are found (which will also
222 * be true for a level 0), arrange to read from /dev/null.
226 if (--baselevel >= 0) {
227 ap_snprintf(number, sizeof(number), "%d", baselevel);
228 inputname = newvstralloc(inputname,
229 basename, "_", number, NULL);
231 inputname = newstralloc(inputname, "/dev/null");
233 if ((in = fopen(inputname, "r")) == NULL) {
234 int save_errno = errno;
236 dbprintf(("%s: error opening %s: %s\n",
237 debug_prefix_time("-gnutar"),
239 strerror(save_errno)));
241 error("error [opening %s: %s]", inputname, strerror(save_errno));
247 * Copy the previous listed incremental file to the new one.
249 if ((out = fopen(incrname, "w")) == NULL) {
250 error("error [opening %s: %s]", incrname, strerror(errno));
253 for (; (line = agets(in)) != NULL; free(line)) {
254 if(fputs(line, out) == EOF || putc('\n', out) == EOF) {
255 error("error [writing to %s: %s]", incrname, strerror(errno));
261 error("error [reading from %s: %s]", inputname, strerror(errno));
263 if (fclose(in) == EOF) {
264 error("error [closing %s: %s]", inputname, strerror(errno));
267 if (fclose(out) == EOF) {
268 error("error [closing %s: %s]", incrname, strerror(errno));
272 dbprintf(("%s: doing level %d dump as listed-incremental",
273 debug_prefix_time("-gnutar"), level));
275 dbprintf((" from %s", inputname));
277 dbprintf((" to %s\n", incrname));
283 /* find previous dump time */
285 if(!start_amandates(0))
286 error("error [opening %s: %s]", AMANDATES_FILE, strerror(errno));
288 amdates = amandates_lookup(disk);
290 prev_dumptime = EPOCH;
291 for(l = 0; l < level; l++) {
292 if(amdates->dates[l] > prev_dumptime)
293 prev_dumptime = amdates->dates[l];
299 gmtm = gmtime(&prev_dumptime);
300 ap_snprintf(dumptimestr, sizeof(dumptimestr),
301 "%04d-%02d-%02d %2d:%02d:%02d GMT",
302 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
303 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
305 dbprintf(("%s: doing level %d dump from date: %s\n",
306 debug_prefix_time("-gnutar"), level, dumptimestr));
308 dirname = amname_to_dirname(amdevice);
310 cur_dumptime = time(0);
312 cur_disk = stralloc(disk);
313 indexcmd = vstralloc(
325 #ifdef SAMBA_CLIENT /* { */
326 /* Use sambatar if the disk to back up is a PC disk */
327 if (amdevice[0] == '/' && amdevice[1]=='/') {
328 char *sharename = NULL, *user_and_password = NULL, *domain = NULL;
329 char *share = NULL, *subdir = NULL;
337 parsesharename(amdevice, &share, &subdir);
343 error("cannot parse disk entry '%s' for share/subdir", disk);
345 if ((subdir) && (SAMBA_VERSION < 2)) {
350 error("subdirectory specified for share '%s' but samba not v2 or better", disk);
352 if ((user_and_password = findpass(share, &domain)) == NULL) {
354 memset(domain, '\0', strlen(domain));
359 error("error [invalid samba host or password not found?]");
361 lpass = strlen(user_and_password);
362 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
363 memset(user_and_password, '\0', lpass);
364 amfree(user_and_password);
366 memset(domain, '\0', strlen(domain));
371 error("password field not \'user%%pass\' for %s", disk);
374 pwtext_len = strlen(pwtext);
375 if ((sharename = makesharename(share, 0)) == 0) {
376 memset(user_and_password, '\0', lpass);
377 amfree(user_and_password);
379 memset(domain, '\0', strlen(domain));
384 error("error [can't make share name of %s]", share);
387 taropt = stralloc("-T");
388 if(options->exclude_file && options->exclude_file->nb_element == 1) {
389 strappend(taropt, "X");
391 #if SAMBA_VERSION >= 2
392 strappend(taropt, "q");
394 strappend(taropt, "c");
396 strappend(taropt, "g");
397 } else if (!options->no_record) {
398 strappend(taropt, "a");
401 dbprintf(("%s: backup of %s", debug_prefix_time("-gnutar"), sharename));
403 dbprintf(("/%s",subdir));
407 program->backup_name = program->restore_name = SAMBA_CLIENT;
408 cmd = stralloc(program->backup_name);
411 start_index(options->createindex, dumpout, mesgf, indexf, indexcmd);
413 if (pwtext_len > 0) {
414 pw_fd_env = "PASSWD_FD";
416 pw_fd_env = "dummy_PASSWD_FD";
418 dumppid = pipespawn(cmd, STDIN_PIPE|PASSWD_PIPE,
419 &dumpin, &dumpout, &mesgf,
423 *user_and_password ? "-U" : skip_argument,
424 *user_and_password ? user_and_password : skip_argument,
426 domain ? "-W" : skip_argument,
427 domain ? domain : skip_argument,
428 #if SAMBA_VERSION >= 2
429 subdir ? "-D" : skip_argument,
430 subdir ? subdir : skip_argument,
435 options->exclude_file && options->exclude_file->nb_element == 1 ? options->exclude_file->first->name : skip_argument,
438 memset(domain, '\0', strlen(domain));
441 if(pwtext_len > 0 && fullwrite(passwdf, pwtext, pwtext_len) < 0) {
442 int save_errno = errno;
445 memset(user_and_password, '\0', lpass);
446 amfree(user_and_password);
449 error("error [password write failed: %s]", strerror(save_errno));
451 memset(user_and_password, '\0', lpass);
452 amfree(user_and_password);
466 char *file_exclude = NULL;
467 char *file_include = NULL;
469 if(options->exclude_file) nb_exclude+=options->exclude_file->nb_element;
470 if(options->exclude_list) nb_exclude+=options->exclude_list->nb_element;
471 if(options->include_file) nb_include+=options->include_file->nb_element;
472 if(options->include_list) nb_include+=options->include_list->nb_element;
474 if(nb_exclude > 0) file_exclude = build_exclude(disk, amdevice, options, 0);
475 if(nb_include > 0) file_include = build_include(disk, amdevice, options, 0);
477 my_argv = alloc(sizeof(char *) * (17 + (nb_exclude*2)+(nb_include*2)));
479 cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
482 start_index(options->createindex, dumpout, mesgf, indexf, indexcmd);
484 my_argv[i++] = "gtar";
485 my_argv[i++] = "--create";
486 my_argv[i++] = "--file";
488 my_argv[i++] = "--directory";
489 my_argv[i++] = dirname;
490 my_argv[i++] = "--one-file-system";
491 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
492 my_argv[i++] = "--listed-incremental";
493 my_argv[i++] = incrname;
495 my_argv[i++] = "--incremental";
496 my_argv[i++] = "--newer";
497 my_argv[i++] = dumptimestr;
499 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
500 /* --atime-preserve causes gnutar to call
501 * utime() after reading files in order to
502 * adjust their atime. However, utime()
503 * updates the file's ctime, so incremental
504 * dumps will think the file has changed. */
505 my_argv[i++] = "--atime-preserve";
507 my_argv[i++] = "--sparse";
508 my_argv[i++] = "--ignore-failed-read";
509 my_argv[i++] = "--totals";
512 my_argv[i++] = "--exclude-from";
513 my_argv[i++] = file_exclude;
517 my_argv[i++] = "--files-from";
518 my_argv[i++] = file_include;
524 dumppid = pipespawnv(cmd, STDIN_PIPE,
525 &dumpin, &dumpout, &mesgf, my_argv);
527 amfree(file_exclude);
528 amfree(file_include);
531 dbprintf(("%s: %s: pid %ld\n",
532 debug_prefix_time("-gnutar"),
541 /* close the write ends of the pipes */
547 if (options->createindex)
551 static void end_backup(goterror)
554 if(!options->no_record && !goterror) {
555 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
556 if (incrname != NULL && strlen(incrname) > 4) {
559 nodotnew = stralloc(incrname);
560 nodotnew[strlen(nodotnew)-4] = '\0';
561 if (rename(incrname, nodotnew) != 0) {
562 fprintf(stderr, "%s: warning [renaming %s to %s: %s]\n",
563 get_pname(), incrname, nodotnew, strerror(errno));
570 if(!start_amandates(1)) {
571 fprintf(stderr, "%s: warning [opening %s: %s]\n", get_pname(),
572 AMANDATES_FILE, strerror(errno));
575 amandates_updateone(cur_disk, cur_level, cur_dumptime);
582 backup_program_t gnutar_program = {
589 re_table, start_backup, end_backup