2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * util.c miscellaneous utility subroutines for Bacula
38 #include "findlib/find.h"
41 * Various Bacula Utility subroutines
45 /* Return true of buffer has all zero bytes */
46 bool is_buf_zero(char *buf, int len)
50 int i, len64, done, rem;
56 /* Optimize by checking uint64_t for zero */
57 len64 = len / sizeof(uint64_t);
58 for (i=0; i < len64; i++) {
63 done = len64 * sizeof(uint64_t); /* bytes already checked */
66 for (i = 0; i < rem; i++) {
75 /* Convert a string in place to lower case */
79 if (B_ISUPPER(*str)) {
80 *str = tolower((int)(*str));
86 /* Convert spaces to non-space character.
87 * This makes scanf of fields containing spaces easier.
90 bash_spaces(char *str)
99 /* Convert spaces to non-space character.
100 * This makes scanf of fields containing spaces easier.
103 bash_spaces(POOL_MEM &pm)
105 char *str = pm.c_str();
114 /* Convert non-space characters (0x1) back into spaces */
116 unbash_spaces(char *str)
125 /* Convert non-space characters (0x1) back into spaces */
127 unbash_spaces(POOL_MEM &pm)
129 char *str = pm.c_str();
137 char *encode_time(time_t time, char *buf)
142 #if defined(HAVE_WIN32)
144 * Avoid a seg fault in Microsoft's CRT localtime_r(),
145 * which incorrectly references a NULL returned from gmtime() if
146 * time is negative before or after the timezone adjustment.
150 if ((gtm = gmtime(&time)) == NULL) {
154 if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
159 if (localtime_r(&time, &tm)) {
160 n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
161 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
162 tm.tm_hour, tm.tm_min, tm.tm_sec);
170 * Convert a JobStatus code into a human readable form
172 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
179 jobstat = _("Created");
182 jobstat = _("Running");
185 jobstat = _("Blocked");
191 case JS_ErrorTerminated:
192 jobstat = _("Error");
195 jobstat = _("Non-fatal error");
198 jobstat = _("Canceled");
201 jobstat = _("Verify differences");
204 jobstat = _("Waiting on FD");
207 jobstat = _("Wait on SD");
210 jobstat = _("Wait for new Volume");
213 jobstat = _("Waiting for mount");
215 case JS_WaitStoreRes:
216 jobstat = _("Waiting for Storage resource");
219 jobstat = _("Waiting for Job resource");
221 case JS_WaitClientRes:
222 jobstat = _("Waiting for Client resource");
225 jobstat = _("Waiting on Max Jobs");
227 case JS_WaitStartTime:
228 jobstat = _("Waiting for Start Time");
230 case JS_WaitPriority:
231 jobstat = _("Waiting on Priority");
233 case JS_DataCommitting:
234 jobstat = _("SD committing Data");
236 case JS_DataDespooling:
237 jobstat = _("SD despooling Data");
239 case JS_AttrDespooling:
240 jobstat = _("SD despooling Attributes");
242 case JS_AttrInserting:
243 jobstat = _("Dir inserting Attributes");
247 if (JobStatus == 0) {
250 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
255 bstrncpy(msg, jobstat, maxlen);
259 * Convert a JobStatus code into a human readable form - gui version
261 void jobstatus_to_ascii_gui(int JobStatus, char *msg, int maxlen)
263 const char *cnv = NULL;
266 cnv = _("Completed successfully");
268 case JS_ErrorTerminated:
269 cnv = _("Terminated with errors");
272 cnv = _("Fatal error");
275 cnv = _("Created, not yet running");
278 cnv = _("Canceled by user");
281 cnv = _("Verify found differences");
284 cnv = _("Waiting for File daemon");
287 cnv = _("Waiting for Storage daemon");
289 case JS_WaitPriority:
290 cnv = _("Waiting for higher priority jobs");
292 case JS_AttrInserting:
293 cnv = _("Batch inserting file records");
298 bstrncpy(msg, cnv, maxlen);
300 jobstatus_to_ascii( JobStatus, msg, maxlen);
306 * Convert Job Termination Status into a string
308 const char *job_status_to_str(int stat)
316 case JS_ErrorTerminated:
321 str = _("Fatal Error");
327 str = _("Differences");
330 str = _("Unknown term code");
338 * Convert Job Type into a string
340 const char *job_type_to_str(int type)
367 str = _("System or Console");
373 str = _("Unknown Type");
380 * Convert Job Level into a string
382 const char *job_level_to_str(int level)
393 str = _("Incremental");
396 str = _("Differential");
401 case L_VERIFY_CATALOG:
402 str = _("Verify Catalog");
405 str = _("Verify Init Catalog");
407 case L_VERIFY_VOLUME_TO_CATALOG:
408 str = _("Verify Volume to Catalog");
410 case L_VERIFY_DISK_TO_CATALOG:
411 str = _("Verify Disk to Catalog");
414 str = _("Verify Data");
420 str = _("Unknown Job Level");
426 const char *volume_status_to_str(const char *status)
430 NT_("Append"), _("Append"),
431 NT_("Archive"), _("Archive"),
432 NT_("Disabled"), _("Disabled"),
433 NT_("Full"), _("Full"),
434 NT_("Used"), _("Used"),
435 NT_("Cleaning"), _("Cleaning"),
436 NT_("Recycle"), _("Recycle"),
437 NT_("Read-Only"), _("Read-Only"),
438 NT_("Error"), _("Error"),
442 for (pos = 0 ; vs[pos] ; pos += 2) {
443 if ( !strcmp(vs[pos],status) ) {
449 return _("Invalid volume status");
453 /***********************************************************************
454 * Encode the mode bits into a 10 character string like LS does
455 ***********************************************************************/
457 char *encode_mode(mode_t mode, char *buf)
461 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
462 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
463 *cp++ = mode & S_IRUSR ? 'r' : '-';
464 *cp++ = mode & S_IWUSR ? 'w' : '-';
465 *cp++ = (mode & S_ISUID
466 ? (mode & S_IXUSR ? 's' : 'S')
467 : (mode & S_IXUSR ? 'x' : '-'));
468 *cp++ = mode & S_IRGRP ? 'r' : '-';
469 *cp++ = mode & S_IWGRP ? 'w' : '-';
470 *cp++ = (mode & S_ISGID
471 ? (mode & S_IXGRP ? 's' : 'S')
472 : (mode & S_IXGRP ? 'x' : '-'));
473 *cp++ = mode & S_IROTH ? 'r' : '-';
474 *cp++ = mode & S_IWOTH ? 'w' : '-';
475 *cp++ = (mode & S_ISVTX
476 ? (mode & S_IXOTH ? 't' : 'T')
477 : (mode & S_IXOTH ? 'x' : '-'));
482 #if defined(HAVE_WIN32)
483 int do_shell_expansion(char *name, int name_len)
485 char *src = bstrdup(name);
487 ExpandEnvironmentStrings(src, name, name_len);
494 int do_shell_expansion(char *name, int name_len)
496 static char meta[] = "~\\$[]*?`'<>\"";
501 char line[MAXSTRING];
502 const char *shellcmd;
504 /* Check if any meta characters are present */
506 for (i = 0; i < len; i++) {
507 if (strchr(name, meta[i])) {
513 cmd = get_pool_memory(PM_FNAME);
515 if ((shellcmd = getenv("SHELL")) == NULL) {
516 shellcmd = "/bin/sh";
518 pm_strcpy(&cmd, shellcmd);
519 pm_strcat(&cmd, " -c \"echo ");
520 pm_strcat(&cmd, name);
521 pm_strcat(&cmd, "\"");
522 Dmsg1(400, "Send: %s\n", cmd);
523 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
525 fgets(line, sizeof(line), bpipe->rfd);
526 strip_trailing_junk(line);
527 stat = close_bpipe(bpipe);
528 Dmsg2(400, "stat=%d got: %s\n", stat, line);
530 stat = 1; /* error */
532 free_pool_memory(cmd);
534 bstrncpy(name, line, name_len);
542 /* MAKESESSIONKEY -- Generate session key with optional start
543 key. If mode is TRUE, the key will be
544 translated to a string, otherwise it is
545 returned as 16 binary bytes.
547 from SpeakFreely by John Walker */
549 void make_session_key(char *key, char *seed, int mode)
552 struct MD5Context md5c;
553 unsigned char md5key[16], md5key1[16];
560 bstrncat(s, seed, sizeof(s));
563 /* The following creates a seed for the session key generator
564 based on a collection of volatile and environment-specific
565 information unlikely to be vulnerable (as a whole) to an
566 exhaustive search attack. If one of these items isn't
567 available on your machine, replace it with something
568 equivalent or, if you like, just delete it. */
570 #if defined(HAVE_WIN32)
578 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
579 (void)getcwd(s + strlen(s), 256);
580 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
581 QueryPerformanceCounter(&li);
582 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
583 GetSystemTimeAsFileTime(&ft);
584 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
585 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
587 GetComputerName(s + strlen(s), &length);
589 GetUserName(s + strlen(s), &length);
592 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
593 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
594 (void)getcwd(s + strlen(s), 256);
595 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
596 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
598 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
600 #if defined(HAVE_GETHOSTID)
601 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
603 gethostname(s + strlen(s), 256);
604 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
605 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
608 MD5Update(&md5c, (uint8_t *)s, strlen(s));
609 MD5Final(md5key, &md5c);
610 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
612 MD5Update(&md5c, (uint8_t *)s, strlen(s));
613 MD5Final(md5key1, &md5c);
614 #define nextrand (md5key[j] ^ md5key1[j])
616 for (j = k = 0; j < 16; j++) {
617 unsigned char rb = nextrand;
619 #define Rad16(x) ((x) + 'A')
620 key[k++] = Rad16((rb >> 4) & 0xF);
621 key[k++] = Rad16(rb & 0xF);
629 for (j = 0; j < 16; j++) {
636 void encode_session_key(char *encode, char *session, char *key, int maxlen)
639 for (i=0; (i < maxlen-1) && session[i]; i++) {
640 if (session[i] == '-') {
643 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
647 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
650 void decode_session_key(char *decode, char *session, char *key, int maxlen)
654 for (i=0; (i < maxlen-1) && session[i]; i++) {
655 if (session[i] == '-') {
658 x = (session[i] - 'A' - key[i]) & 0xF;
666 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
672 * Edit job codes into main command line
675 * %d = Director's name
680 * %n = Unadorned Job name
682 * %t = Job type (Backup, ...)
686 * omsg = edited output message
687 * imsg = input string containing edit codes (%x)
688 * to = recepients list
691 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
696 char name[MAX_NAME_LENGTH];
700 Dmsg1(200, "edit_job_codes: %s\n", imsg);
701 for (p=imsg; *p; p++) {
709 str = jcr->client_name;
715 str = my_name; /* Director's name */
719 str = job_status_to_str(jcr->JobStatus);
726 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
732 case 'j': /* Job name */
741 str = job_level_to_str(jcr->JobLevel);
748 bstrncpy(name, jcr->Job, sizeof(name));
749 /* There are three periods after the Job name */
750 for (i=0; i<3; i++) {
751 if ((q=strrchr(name, '.')) != NULL) {
763 case 's': /* since time */
764 if (jcr && jcr->stime) {
772 str = job_type_to_str(jcr->JobType);
779 if (jcr->VolumeName && jcr->VolumeName[0]) {
780 str = jcr->VolumeName;
790 if (callback != NULL) {
791 str = callback(jcr, p);
807 Dmsg1(1200, "add_str %s\n", str);
808 pm_strcat(&omsg, str);
809 Dmsg1(1200, "omsg=%s\n", omsg);
814 void set_working_directory(char *wd)
816 struct stat stat_buf;
819 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
821 if (stat(wd, &stat_buf) != 0) {
822 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
825 if (!S_ISDIR(stat_buf.st_mode)) {
826 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
829 working_directory = wd; /* set global */
832 const char *last_path_separator(const char *str)
835 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
836 if (IsPathSeparator(*p)) {