2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1999 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.92 2005/12/09 03:22:52 paddy_s 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 */
44 static regex_t re_table[] = {
45 /* tar prints the size in bytes */
46 AM_SIZE_RE("^ *Total bytes written: [0-9][0-9]*", 1),
47 AM_NORMAL_RE("^Elapsed time:"),
48 AM_NORMAL_RE("^Throughput"),
50 /* GNU tar 1.13.17 will print this warning when (not) backing up a
52 AM_NORMAL_RE(": socket ignored$"),
54 /* GNUTAR produces a few error messages when files are modified or
55 removed while it is running. They may cause data to be lost, but
56 then they may not. We shouldn't consider them NORMAL until
57 further investigation. */
58 #ifdef IGNORE_TAR_ERRORS
59 AM_NORMAL_RE(": File .* shrunk by [0-9][0-9]* bytes, padding with zeros"),
60 AM_NORMAL_RE(": Cannot add file .*: No such file or directory$"),
61 AM_NORMAL_RE(": Error exit delayed from previous errors"),
64 /* samba may produce these output messages */
65 AM_NORMAL_RE("^[Aa]dded interface"),
66 AM_NORMAL_RE("^session request to "),
67 AM_NORMAL_RE("^ *tar: dumped [0-9][0-9]* (tar )?files"),
68 AM_NORMAL_RE("^Domain="),
71 AM_NORMAL_RE("^doing parameter"),
72 AM_NORMAL_RE("^pm_process\\(\\)"),
73 AM_NORMAL_RE("^adding IPC"),
74 AM_NORMAL_RE("^Opening"),
75 AM_NORMAL_RE("^Connect"),
77 AM_NORMAL_RE("^security="),
78 AM_NORMAL_RE("^capabilities"),
79 AM_NORMAL_RE("^Sec mode "),
80 AM_NORMAL_RE("^Got "),
81 AM_NORMAL_RE("^Chose protocol "),
82 AM_NORMAL_RE("^Server "),
83 AM_NORMAL_RE("^Timezone "),
84 AM_NORMAL_RE("^received"),
85 AM_NORMAL_RE("^FINDFIRST"),
86 AM_NORMAL_RE("^FINDNEXT"),
87 AM_NORMAL_RE("^dos_clean_name"),
88 AM_NORMAL_RE("^file"),
89 AM_NORMAL_RE("^getting file"),
90 AM_NORMAL_RE("^Rejected chained"),
91 AM_NORMAL_RE("^nread="),
92 AM_NORMAL_RE("^\\([0-9][0-9]* kb/s\\)"),
93 AM_NORMAL_RE("^\\([0-9][0-9]*\\.[0-9][0-9]* kb/s\\)"),
94 AM_NORMAL_RE("^[ \t]*[0-9][0-9]* \\([ \t]*[0-9][0-9]*\\.[0-9][0-9]* kb/s\\)"),
95 AM_NORMAL_RE("^[ \t]*directory "),
96 AM_NORMAL_RE("^load_client_codepage"),
99 #ifdef IGNORE_SMBCLIENT_ERRORS
100 /* This will cause amanda to ignore real errors, but that may be
101 * unavoidable when you're backing up system disks. It seems to be
102 * a safe thing to do if you know what you're doing. */
103 AM_NORMAL_RE("^ERRDOS - ERRbadshare opening remote file"),
104 AM_NORMAL_RE("^ERRDOS - ERRbadfile opening remote file"),
105 AM_NORMAL_RE("^ERRDOS - ERRnoaccess opening remote file"),
106 AM_NORMAL_RE("^ERRSRV - ERRaccess setting attributes on file"),
107 AM_NORMAL_RE("^ERRDOS - ERRnoaccess setting attributes on file"),
110 #if SAMBA_VERSION >= 2
111 /* Backup attempt of nonexisting directory */
112 AM_ERROR_RE("ERRDOS - ERRbadpath (Directory invalid.)"),
113 AM_NORMAL_RE("^Domain="),
116 /* catch-all: DMP_STRANGE is returned for all other lines */
126 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
127 static char *incrname = NULL;
130 * doing similar to $ gtar | compression | encryption
132 static void start_backup(host, disk, amdevice, level, dumpdate, dataf, mesgf, indexf)
134 char *disk, *amdevice;
135 int level, dataf, mesgf, indexf;
138 int dumpin, dumpout, compout;
140 char *indexcmd = NULL;
141 char *dirname = NULL;
143 char dumptimestr[80];
145 amandates_t *amdates;
146 time_t prev_dumptime;
147 char *error_pn = NULL;
148 char *compopt = NULL;
149 char *encryptopt = skip_argument;
151 error_pn = stralloc2(get_pname(), "-smbclient");
154 fprintf(stderr, "%s: start [%s:%s level %d]\n",
155 get_pname(), host, disk, level);
156 /* apply client-side encryption here */
157 if ( options->encrypt == ENCRYPT_CUST ) {
158 encpid = pipespawn(options->clnt_encrypt, STDIN_PIPE,
159 &compout, &dataf, &mesgf,
160 options->clnt_encrypt, encryptopt, NULL);
161 dbprintf(("%s: pid %ld: %s\n",
162 debug_prefix_time("-gnutar"), (long)encpid, options->clnt_encrypt));
167 /* now do the client-side compression */
168 if(options->compress == COMPR_FAST || options->compress == COMPR_BEST) {
169 compopt = skip_argument;
170 #if defined(COMPRESS_BEST_OPT) && defined(COMPRESS_FAST_OPT)
171 if(options->compress == COMPR_BEST) {
172 compopt = COMPRESS_BEST_OPT;
174 compopt = COMPRESS_FAST_OPT;
177 comppid = pipespawn(COMPRESS_PATH, STDIN_PIPE,
178 &dumpout, &compout, &mesgf,
179 COMPRESS_PATH, compopt, NULL);
180 dbprintf(("%s: pid %ld: %s",
181 debug_prefix_time("-gnutar"), (long)comppid, COMPRESS_PATH));
182 if(compopt != skip_argument) {
183 dbprintf((" %s", compopt));
186 } else if (options->compress == COMPR_CUST) {
187 compopt = skip_argument;
188 comppid = pipespawn(options->clntcompprog, STDIN_PIPE,
189 &dumpout, &compout, &mesgf,
190 options->clntcompprog, compopt, NULL);
191 dbprintf(("%s: pid %ld: %s",
192 debug_prefix_time("-gnutar-cust"), (long)comppid, options->clntcompprog));
193 if(compopt != skip_argument) {
194 dbprintf((" %s", compopt));
202 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR /* { */
203 #ifdef SAMBA_CLIENT /* { */
204 if (amdevice[0] == '/' && amdevice[1]=='/')
209 char *basename = NULL;
210 char number[NUM_STR_SIZE];
213 char *inputname = NULL;
219 basename = vstralloc(GNUTAR_LISTED_INCREMENTAL_DIR,
225 * The loop starts at the first character of the host name,
228 s = basename + sizeof(GNUTAR_LISTED_INCREMENTAL_DIR);
229 while((ch = *s++) != '\0') {
230 if(ch == '/' || isspace(ch)) s[-1] = '_';
233 snprintf(number, sizeof(number), "%d", level);
234 incrname = vstralloc(basename, "_", number, ".new", NULL);
238 * Open the listed incremental file for the previous level. Search
239 * backward until one is found. If none are found (which will also
240 * be true for a level 0), arrange to read from /dev/null.
244 if (--baselevel >= 0) {
245 snprintf(number, sizeof(number), "%d", baselevel);
246 inputname = newvstralloc(inputname,
247 basename, "_", number, NULL);
249 inputname = newstralloc(inputname, "/dev/null");
251 if ((in = fopen(inputname, "r")) == NULL) {
252 int save_errno = errno;
254 dbprintf(("%s: error opening %s: %s\n",
255 debug_prefix_time("-gnutar"),
257 strerror(save_errno)));
259 error("error [opening %s: %s]", inputname, strerror(save_errno));
265 * Copy the previous listed incremental file to the new one.
267 if ((out = fopen(incrname, "w")) == NULL) {
268 error("error [opening %s: %s]", incrname, strerror(errno));
271 for (; (line = agets(in)) != NULL; free(line)) {
272 if(fputs(line, out) == EOF || putc('\n', out) == EOF) {
273 error("error [writing to %s: %s]", incrname, strerror(errno));
279 error("error [reading from %s: %s]", inputname, strerror(errno));
281 if (fclose(in) == EOF) {
282 error("error [closing %s: %s]", inputname, strerror(errno));
285 if (fclose(out) == EOF) {
286 error("error [closing %s: %s]", incrname, strerror(errno));
290 dbprintf(("%s: doing level %d dump as listed-incremental",
291 debug_prefix_time("-gnutar"), level));
293 dbprintf((" from %s", inputname));
295 dbprintf((" to %s\n", incrname));
301 /* find previous dump time */
303 if(!start_amandates(0))
304 error("error [opening %s: %s]", AMANDATES_FILE, strerror(errno));
306 amdates = amandates_lookup(disk);
308 prev_dumptime = EPOCH;
309 for(l = 0; l < level; l++) {
310 if(amdates->dates[l] > prev_dumptime)
311 prev_dumptime = amdates->dates[l];
317 gmtm = gmtime(&prev_dumptime);
318 snprintf(dumptimestr, sizeof(dumptimestr),
319 "%04d-%02d-%02d %2d:%02d:%02d GMT",
320 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
321 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
323 dbprintf(("%s: doing level %d dump from date: %s\n",
324 debug_prefix_time("-gnutar"), level, dumptimestr));
326 dirname = amname_to_dirname(amdevice);
328 cur_dumptime = time(0);
330 cur_disk = stralloc(disk);
331 indexcmd = vstralloc(
343 #ifdef SAMBA_CLIENT /* { */
344 /* Use sambatar if the disk to back up is a PC disk */
345 if (amdevice[0] == '/' && amdevice[1]=='/') {
346 char *sharename = NULL, *user_and_password = NULL, *domain = NULL;
347 char *share = NULL, *subdir = NULL;
355 parsesharename(amdevice, &share, &subdir);
361 error("cannot parse disk entry '%s' for share/subdir", disk);
363 if ((subdir) && (SAMBA_VERSION < 2)) {
368 error("subdirectory specified for share '%s' but samba not v2 or better", disk);
370 if ((user_and_password = findpass(share, &domain)) == NULL) {
372 memset(domain, '\0', strlen(domain));
377 error("error [invalid samba host or password not found?]");
379 lpass = strlen(user_and_password);
380 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
381 memset(user_and_password, '\0', lpass);
382 amfree(user_and_password);
384 memset(domain, '\0', strlen(domain));
389 error("password field not \'user%%pass\' for %s", disk);
392 pwtext_len = strlen(pwtext);
393 if ((sharename = makesharename(share, 0)) == 0) {
394 memset(user_and_password, '\0', lpass);
395 amfree(user_and_password);
397 memset(domain, '\0', strlen(domain));
402 error("error [can't make share name of %s]", share);
405 taropt = stralloc("-T");
406 if(options->exclude_file && options->exclude_file->nb_element == 1) {
407 strappend(taropt, "X");
409 #if SAMBA_VERSION >= 2
410 strappend(taropt, "q");
412 strappend(taropt, "c");
414 strappend(taropt, "g");
415 } else if (!options->no_record) {
416 strappend(taropt, "a");
419 dbprintf(("%s: backup of %s", debug_prefix_time("-gnutar"), sharename));
421 dbprintf(("/%s",subdir));
425 program->backup_name = program->restore_name = SAMBA_CLIENT;
426 cmd = stralloc(program->backup_name);
429 start_index(options->createindex, dumpout, mesgf, indexf, indexcmd);
431 if (pwtext_len > 0) {
432 pw_fd_env = "PASSWD_FD";
434 pw_fd_env = "dummy_PASSWD_FD";
436 dumppid = pipespawn(cmd, STDIN_PIPE|PASSWD_PIPE,
437 &dumpin, &dumpout, &mesgf,
441 *user_and_password ? "-U" : skip_argument,
442 *user_and_password ? user_and_password : skip_argument,
444 domain ? "-W" : skip_argument,
445 domain ? domain : skip_argument,
446 #if SAMBA_VERSION >= 2
447 subdir ? "-D" : skip_argument,
448 subdir ? subdir : skip_argument,
453 options->exclude_file && options->exclude_file->nb_element == 1 ? options->exclude_file->first->name : skip_argument,
456 memset(domain, '\0', strlen(domain));
459 if(pwtext_len > 0 && fullwrite(passwdf, pwtext, pwtext_len) < 0) {
460 int save_errno = errno;
463 memset(user_and_password, '\0', lpass);
464 amfree(user_and_password);
467 error("error [password write failed: %s]", strerror(save_errno));
469 memset(user_and_password, '\0', lpass);
470 amfree(user_and_password);
478 #endif /*end of samba */
485 char *file_exclude = NULL;
486 char *file_include = NULL;
488 if(options->exclude_file) nb_exclude+=options->exclude_file->nb_element;
489 if(options->exclude_list) nb_exclude+=options->exclude_list->nb_element;
490 if(options->include_file) nb_include+=options->include_file->nb_element;
491 if(options->include_list) nb_include+=options->include_list->nb_element;
493 if(nb_exclude > 0) file_exclude = build_exclude(disk, amdevice, options, 0);
494 if(nb_include > 0) file_include = build_include(disk, amdevice, options, 0);
496 my_argv = alloc(sizeof(char *) * (17 + (nb_exclude*2)+(nb_include*2)));
498 cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
501 start_index(options->createindex, dumpout, mesgf, indexf, indexcmd);
503 my_argv[i++] = "gtar";
504 my_argv[i++] = "--create";
505 my_argv[i++] = "--file";
507 my_argv[i++] = "--directory";
508 my_argv[i++] = dirname;
509 my_argv[i++] = "--one-file-system";
510 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
511 my_argv[i++] = "--listed-incremental";
512 my_argv[i++] = incrname;
514 my_argv[i++] = "--incremental";
515 my_argv[i++] = "--newer";
516 my_argv[i++] = dumptimestr;
518 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
519 /* --atime-preserve causes gnutar to call
520 * utime() after reading files in order to
521 * adjust their atime. However, utime()
522 * updates the file's ctime, so incremental
523 * dumps will think the file has changed. */
524 my_argv[i++] = "--atime-preserve";
526 my_argv[i++] = "--sparse";
527 my_argv[i++] = "--ignore-failed-read";
528 my_argv[i++] = "--totals";
531 my_argv[i++] = "--exclude-from";
532 my_argv[i++] = file_exclude;
536 my_argv[i++] = "--files-from";
537 my_argv[i++] = file_include;
543 dumppid = pipespawnv(cmd, STDIN_PIPE,
544 &dumpin, &dumpout, &mesgf, my_argv);
546 amfree(file_exclude);
547 amfree(file_include);
550 dbprintf(("%s: %s: pid %ld\n",
551 debug_prefix_time("-gnutar"),
560 /* close the write ends of the pipes */
567 if (options->createindex)
571 static void end_backup(goterror)
574 if(!options->no_record && !goterror) {
575 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
576 if (incrname != NULL && strlen(incrname) > 4) {
579 nodotnew = stralloc(incrname);
580 nodotnew[strlen(nodotnew)-4] = '\0';
581 if (rename(incrname, nodotnew)) {
582 fprintf(stderr, "%s: warning [renaming %s to %s: %s]\n",
583 get_pname(), incrname, nodotnew, strerror(errno));
590 if(!start_amandates(1)) {
591 fprintf(stderr, "%s: warning [opening %s: %s]", get_pname(),
592 AMANDATES_FILE, strerror(errno));
595 amandates_updateone(cur_disk, cur_level, cur_dumptime);
602 backup_program_t gnutar_program = {
609 re_table, start_backup, end_backup