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.98 2006/07/25 18:35:21 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 */
39 #include "clientconf.h"
45 static amregex_t re_table[] = {
46 /* tar prints the size in bytes */
47 AM_SIZE_RE("^ *Total bytes written: [0-9][0-9]*", 1),
48 AM_NORMAL_RE("^Elapsed time:"),
49 AM_NORMAL_RE("^Throughput"),
51 /* GNU tar 1.13.17 will print this warning when (not) backing up a
53 AM_NORMAL_RE(": socket ignored$"),
55 /* GNUTAR produces a few error messages when files are modified or
56 removed while it is running. They may cause data to be lost, but
57 then they may not. We shouldn't consider them NORMAL until
58 further investigation. */
59 #ifdef IGNORE_TAR_ERRORS
60 AM_NORMAL_RE(": File .* shrunk by [0-9][0-9]* bytes, padding with zeros"),
61 AM_NORMAL_RE(": Cannot add file .*: No such file or directory$"),
62 AM_NORMAL_RE(": Error exit delayed from previous errors"),
65 /* samba may produce these output messages */
66 AM_NORMAL_RE("^[Aa]dded interface"),
67 AM_NORMAL_RE("^session request to "),
68 AM_NORMAL_RE("^tar: dumped [0-9][0-9]* (tar )?files"),
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"),
76 AM_NORMAL_RE("^Domain="),
78 AM_NORMAL_RE("^security="),
79 AM_NORMAL_RE("^capabilities"),
80 AM_NORMAL_RE("^Sec mode "),
81 AM_NORMAL_RE("^Got "),
82 AM_NORMAL_RE("^Chose protocol "),
83 AM_NORMAL_RE("^Server "),
84 AM_NORMAL_RE("^Timezone "),
85 AM_NORMAL_RE("^received"),
86 AM_NORMAL_RE("^FINDFIRST"),
87 AM_NORMAL_RE("^FINDNEXT"),
88 AM_NORMAL_RE("^dos_clean_name"),
89 AM_NORMAL_RE("^file"),
90 AM_NORMAL_RE("^getting file"),
91 AM_NORMAL_RE("^Rejected chained"),
92 AM_NORMAL_RE("^nread="),
93 AM_NORMAL_RE("^\\([0-9][0-9]* kb/s\\)"),
94 AM_NORMAL_RE("^\\([0-9][0-9]*\\.[0-9][0-9]* kb/s\\)"),
95 AM_NORMAL_RE("^[ \t]*[0-9][0-9]* \\([ \t]*[0-9][0-9]*\\.[0-9][0-9]* kb/s\\)"),
96 AM_NORMAL_RE("^[ \t]*directory "),
97 AM_NORMAL_RE("^load_client_codepage"),
100 #ifdef IGNORE_SMBCLIENT_ERRORS
101 /* This will cause amanda to ignore real errors, but that may be
102 * unavoidable when you're backing up system disks. It seems to be
103 * a safe thing to do if you know what you're doing. */
104 AM_NORMAL_RE("^ERRDOS - ERRbadshare opening remote file"),
105 AM_NORMAL_RE("^ERRDOS - ERRbadfile opening remote file"),
106 AM_NORMAL_RE("^ERRDOS - ERRnoaccess opening remote file"),
107 AM_NORMAL_RE("^ERRSRV - ERRaccess setting attributes on file"),
108 AM_NORMAL_RE("^ERRDOS - ERRnoaccess setting attributes on file"),
111 #if SAMBA_VERSION >= 2
112 /* Backup attempt of nonexisting directory */
113 AM_ERROR_RE("ERRDOS - ERRbadpath (Directory invalid.)"),
114 AM_NORMAL_RE("^Domain="),
117 /* catch-all: DMP_STRANGE is returned for all other lines */
127 static char *gnutar_list_dir = NULL;
128 static char *incrname = NULL;
129 static char *amandates_file;
131 * doing similar to $ gtar | compression | encryption
144 int dumpin, dumpout, compout;
146 char *indexcmd = NULL;
147 char *dirname = NULL;
149 char dumptimestr[80];
151 amandates_t *amdates;
152 time_t prev_dumptime;
153 char *error_pn = NULL;
154 char *compopt = NULL;
155 char *encryptopt = skip_argument;
162 (void)dumpdate; /* Quiet unused parameter warning */
164 error_pn = stralloc2(get_pname(), "-smbclient");
166 qdisk = quote_string(disk);
167 dbprintf(("%s: start: %s:%s lev %d\n",
168 get_pname(), host, qdisk, level));
170 fprintf(stderr, "%s: start [%s:%s level %d]\n",
171 get_pname(), host, qdisk, level);
173 /* apply client-side encryption here */
174 if ( options->encrypt == ENCRYPT_CUST ) {
175 encpid = pipespawn(options->clnt_encrypt, STDIN_PIPE,
176 &compout, &dataf, &mesgf,
177 options->clnt_encrypt, encryptopt, NULL);
178 dbprintf(("%s: pid %ld: %s\n",
179 debug_prefix_time("-gnutar"), (long)encpid, options->clnt_encrypt));
184 /* now do the client-side compression */
185 if(options->compress == COMPR_FAST || options->compress == COMPR_BEST) {
186 compopt = skip_argument;
187 #if defined(COMPRESS_BEST_OPT) && defined(COMPRESS_FAST_OPT)
188 if(options->compress == COMPR_BEST) {
189 compopt = COMPRESS_BEST_OPT;
191 compopt = COMPRESS_FAST_OPT;
194 comppid = pipespawn(COMPRESS_PATH, STDIN_PIPE,
195 &dumpout, &compout, &mesgf,
196 COMPRESS_PATH, compopt, NULL);
197 dbprintf(("%s: pid %ld: %s",
198 debug_prefix_time("-gnutar"), (long)comppid, COMPRESS_PATH));
199 if(compopt != skip_argument) {
200 dbprintf((" %s", compopt));
203 } else if (options->compress == COMPR_CUST) {
204 compopt = skip_argument;
205 comppid = pipespawn(options->clntcompprog, STDIN_PIPE,
206 &dumpout, &compout, &mesgf,
207 options->clntcompprog, compopt, NULL);
208 dbprintf(("%s: pid %ld: %s",
209 debug_prefix_time("-gnutar-cust"), (long)comppid, options->clntcompprog));
210 if(compopt != skip_argument) {
211 dbprintf((" %s", compopt));
219 gnutar_list_dir = client_getconf_str(CLN_GNUTAR_LIST_DIR);
220 if (strlen(gnutar_list_dir) == 0)
221 gnutar_list_dir = NULL;
223 #ifdef SAMBA_CLIENT /* { */
224 if (amdevice[0] == '/' && amdevice[1]=='/')
228 if (gnutar_list_dir) {
229 char *basename = NULL;
230 char number[NUM_STR_SIZE];
233 char *inputname = NULL;
236 basename = vstralloc(gnutar_list_dir,
242 * The loop starts at the first character of the host name,
245 s = basename + strlen(gnutar_list_dir) + 1;
246 while((ch = *s++) != '\0') {
251 snprintf(number, SIZEOF(number), "%d", level);
252 incrname = vstralloc(basename, "_", number, ".new", NULL);
256 * Open the listed incremental file from the previous level. Search
257 * backward until one is found. If none are found (which will also
258 * be true for a level 0), arrange to read from /dev/null.
263 if (--baselevel >= 0) {
264 snprintf(number, SIZEOF(number), "%d", baselevel);
265 inputname = newvstralloc(inputname,
266 basename, "_", number, NULL);
268 inputname = newstralloc(inputname, "/dev/null");
270 if ((infd = open(inputname, O_RDONLY)) == -1) {
271 int save_errno = errno;
272 char *qname = quote_string(inputname);
274 dbprintf(("%s: error opening '%s': %s\n",
275 debug_prefix_time("-gnutar"),
277 strerror(save_errno)));
279 error("error [opening '%s': %s]", qname, strerror(save_errno));
287 * Copy the previous listed incremental file to the new one.
289 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
290 error("error [opening '%s': %s]", incrname, strerror(errno));
294 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
295 if (fullwrite(outfd, &buf, (size_t)nb) < nb) {
296 error("error [writing to '%s': %s]", incrname,
303 error("error [reading from '%s': %s]", inputname, strerror(errno));
307 if (close(infd) != 0) {
308 error("error [closing '%s': %s]", inputname, strerror(errno));
311 if (close(outfd) != 0) {
312 error("error [closing '%s': %s]", incrname, strerror(errno));
316 dbprintf(("%s: doing level %d dump as listed-incremental",
317 debug_prefix_time("-gnutar"), level));
319 quoted = quote_string(inputname);
320 dbprintf((" from '%s'", quoted));
323 quoted = quote_string(incrname);
324 dbprintf((" to '%s'\n", quoted));
330 /* find previous dump time */
332 amandates_file = client_getconf_str(CLN_AMANDATES);
333 if(!start_amandates(amandates_file, 0)) {
334 error("error [opening %s: %s]", amandates_file, strerror(errno));
338 amdates = amandates_lookup(disk);
340 prev_dumptime = EPOCH;
341 for(l = 0; l < level; l++) {
342 if(amdates->dates[l] > prev_dumptime)
343 prev_dumptime = amdates->dates[l];
349 gmtm = gmtime(&prev_dumptime);
350 snprintf(dumptimestr, SIZEOF(dumptimestr),
351 "%04d-%02d-%02d %2d:%02d:%02d GMT",
352 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
353 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
355 dbprintf(("%s: doing level %d dump from date: %s\n",
356 debug_prefix_time("-gnutar"), level, dumptimestr));
358 dirname = amname_to_dirname(amdevice);
360 cur_dumptime = time(0);
362 cur_disk = stralloc(disk);
363 indexcmd = vstralloc(
375 #ifdef SAMBA_CLIENT /* { */
376 /* Use sambatar if the disk to back up is a PC disk */
377 if (amdevice[0] == '/' && amdevice[1]=='/') {
378 char *sharename = NULL, *user_and_password = NULL, *domain = NULL;
379 char *share = NULL, *subdir = NULL;
387 parsesharename(amdevice, &share, &subdir);
393 error("cannot parse disk entry %s for share/subdir", qdisk);
396 if ((subdir) && (SAMBA_VERSION < 2)) {
401 error("subdirectory specified for share %s but samba not v2 or better", qdisk);
404 if ((user_and_password = findpass(share, &domain)) == NULL) {
406 memset(domain, '\0', strlen(domain));
411 error("error [invalid samba host or password not found?]");
414 lpass = strlen(user_and_password);
415 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
416 memset(user_and_password, '\0', lpass);
417 amfree(user_and_password);
419 memset(domain, '\0', strlen(domain));
424 error("password field not \'user%%pass\' for %s", qdisk);
428 pwtext_len = strlen(pwtext);
429 if ((sharename = makesharename(share, 0)) == 0) {
430 memset(user_and_password, '\0', lpass);
431 amfree(user_and_password);
433 memset(domain, '\0', strlen(domain));
438 error("error [can't make share name of %s]", share);
442 taropt = stralloc("-T");
443 if(options->exclude_file && options->exclude_file->nb_element == 1) {
444 strappend(taropt, "X");
446 #if SAMBA_VERSION >= 2
447 strappend(taropt, "q");
449 strappend(taropt, "c");
451 strappend(taropt, "g");
452 } else if (!options->no_record) {
453 strappend(taropt, "a");
456 dbprintf(("%s: backup of %s", debug_prefix_time("-gnutar"), sharename));
458 dbprintf(("/%s",subdir));
462 program->backup_name = program->restore_name = SAMBA_CLIENT;
463 cmd = stralloc(program->backup_name);
466 start_index(options->createindex, dumpout, mesgf, indexf, indexcmd);
468 if (pwtext_len > 0) {
469 pw_fd_env = "PASSWD_FD";
471 pw_fd_env = "dummy_PASSWD_FD";
473 dumppid = pipespawn(cmd, STDIN_PIPE|PASSWD_PIPE,
474 &dumpin, &dumpout, &mesgf,
478 *user_and_password ? "-U" : skip_argument,
479 *user_and_password ? user_and_password : skip_argument,
481 domain ? "-W" : skip_argument,
482 domain ? domain : skip_argument,
483 #if SAMBA_VERSION >= 2
484 subdir ? "-D" : skip_argument,
485 subdir ? subdir : skip_argument,
490 options->exclude_file && options->exclude_file->nb_element == 1 ? options->exclude_file->first->name : skip_argument,
493 memset(domain, '\0', strlen(domain));
496 if(pwtext_len > 0 && fullwrite(passwdf, pwtext, pwtext_len) < 0) {
497 int save_errno = errno;
500 memset(user_and_password, '\0', lpass);
501 amfree(user_and_password);
504 error("error [password write failed: %s]", strerror(save_errno));
507 memset(user_and_password, '\0', lpass);
508 amfree(user_and_password);
516 #endif /*end of samba */
523 char *file_exclude = NULL;
524 char *file_include = NULL;
526 if(options->exclude_file) nb_exclude+=options->exclude_file->nb_element;
527 if(options->exclude_list) nb_exclude+=options->exclude_list->nb_element;
528 if(options->include_file) nb_include+=options->include_file->nb_element;
529 if(options->include_list) nb_include+=options->include_list->nb_element;
531 if(nb_exclude > 0) file_exclude = build_exclude(disk, amdevice, options, 0);
532 if(nb_include > 0) file_include = build_include(disk, amdevice, options, 0);
534 my_argv = alloc(SIZEOF(char *) * (22 + (nb_exclude*2)+(nb_include*2)));
536 cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
539 start_index(options->createindex, dumpout, mesgf, indexf, indexcmd);
541 my_argv[i++] = "runtar";
542 if (g_options->config)
543 my_argv[i++] = g_options->config;
545 my_argv[i++] = "NOCONFIG";
546 my_argv[i++] = "gtar";
547 my_argv[i++] = "--create";
548 my_argv[i++] = "--file";
550 my_argv[i++] = "--directory";
551 my_argv[i++] = dirname;
552 my_argv[i++] = "--one-file-system";
553 if (gnutar_list_dir && incrname) {
554 my_argv[i++] = "--listed-incremental";
555 my_argv[i++] = incrname;
557 my_argv[i++] = "--incremental";
558 my_argv[i++] = "--newer";
559 my_argv[i++] = dumptimestr;
561 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
562 /* --atime-preserve causes gnutar to call
563 * utime() after reading files in order to
564 * adjust their atime. However, utime()
565 * updates the file's ctime, so incremental
566 * dumps will think the file has changed. */
567 my_argv[i++] = "--atime-preserve";
569 my_argv[i++] = "--sparse";
570 my_argv[i++] = "--ignore-failed-read";
571 my_argv[i++] = "--totals";
574 my_argv[i++] = "--exclude-from";
575 my_argv[i++] = file_exclude;
579 my_argv[i++] = "--files-from";
580 my_argv[i++] = file_include;
586 dumppid = pipespawnv(cmd, STDIN_PIPE,
587 &dumpin, &dumpout, &mesgf, my_argv);
589 amfree(file_exclude);
590 amfree(file_include);
593 dbprintf(("%s: %s: pid %ld\n",
594 debug_prefix_time("-gnutar"),
604 /* close the write ends of the pipes */
611 if (options->createindex)
619 if(!options->no_record && !goterror) {
620 if (incrname != NULL && strlen(incrname) > 4) {
623 nodotnew = stralloc(incrname);
624 nodotnew[strlen(nodotnew)-4] = '\0';
625 if (rename(incrname, nodotnew)) {
626 fprintf(stderr, "%s: warning [renaming %s to %s: %s]\n",
627 get_pname(), incrname, nodotnew, strerror(errno));
633 if(!start_amandates(amandates_file, 1)) {
634 fprintf(stderr, "%s: warning [opening %s: %s]", get_pname(),
635 amandates_file, strerror(errno));
638 amandates_updateone(cur_disk, cur_level, cur_dumptime);
645 backup_program_t gnutar_program = {
652 re_table, start_backup, end_backup