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 Kern Sibbald.
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_("Purged"), _("Purged"),
437 NT_("Recycle"), _("Recycle"),
438 NT_("Read-Only"), _("Read-Only"),
439 NT_("Error"), _("Error"),
443 for (pos = 0 ; vs[pos] ; pos += 2) {
444 if ( !strcmp(vs[pos],status) ) {
450 return _("Invalid volume status");
454 /***********************************************************************
455 * Encode the mode bits into a 10 character string like LS does
456 ***********************************************************************/
458 char *encode_mode(mode_t mode, char *buf)
462 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
463 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
464 *cp++ = mode & S_IRUSR ? 'r' : '-';
465 *cp++ = mode & S_IWUSR ? 'w' : '-';
466 *cp++ = (mode & S_ISUID
467 ? (mode & S_IXUSR ? 's' : 'S')
468 : (mode & S_IXUSR ? 'x' : '-'));
469 *cp++ = mode & S_IRGRP ? 'r' : '-';
470 *cp++ = mode & S_IWGRP ? 'w' : '-';
471 *cp++ = (mode & S_ISGID
472 ? (mode & S_IXGRP ? 's' : 'S')
473 : (mode & S_IXGRP ? 'x' : '-'));
474 *cp++ = mode & S_IROTH ? 'r' : '-';
475 *cp++ = mode & S_IWOTH ? 'w' : '-';
476 *cp++ = (mode & S_ISVTX
477 ? (mode & S_IXOTH ? 't' : 'T')
478 : (mode & S_IXOTH ? 'x' : '-'));
483 #if defined(HAVE_WIN32)
484 int do_shell_expansion(char *name, int name_len)
486 char *src = bstrdup(name);
488 ExpandEnvironmentStrings(src, name, name_len);
495 int do_shell_expansion(char *name, int name_len)
497 static char meta[] = "~\\$[]*?`'<>\"";
502 char line[MAXSTRING];
503 const char *shellcmd;
505 /* Check if any meta characters are present */
507 for (i = 0; i < len; i++) {
508 if (strchr(name, meta[i])) {
514 cmd = get_pool_memory(PM_FNAME);
516 if ((shellcmd = getenv("SHELL")) == NULL) {
517 shellcmd = "/bin/sh";
519 pm_strcpy(&cmd, shellcmd);
520 pm_strcat(&cmd, " -c \"echo ");
521 pm_strcat(&cmd, name);
522 pm_strcat(&cmd, "\"");
523 Dmsg1(400, "Send: %s\n", cmd);
524 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
526 fgets(line, sizeof(line), bpipe->rfd);
527 strip_trailing_junk(line);
528 stat = close_bpipe(bpipe);
529 Dmsg2(400, "stat=%d got: %s\n", stat, line);
531 stat = 1; /* error */
533 free_pool_memory(cmd);
535 bstrncpy(name, line, name_len);
543 /* MAKESESSIONKEY -- Generate session key with optional start
544 key. If mode is TRUE, the key will be
545 translated to a string, otherwise it is
546 returned as 16 binary bytes.
548 from SpeakFreely by John Walker */
550 void make_session_key(char *key, char *seed, int mode)
553 struct MD5Context md5c;
554 unsigned char md5key[16], md5key1[16];
561 bstrncat(s, seed, sizeof(s));
564 /* The following creates a seed for the session key generator
565 based on a collection of volatile and environment-specific
566 information unlikely to be vulnerable (as a whole) to an
567 exhaustive search attack. If one of these items isn't
568 available on your machine, replace it with something
569 equivalent or, if you like, just delete it. */
571 #if defined(HAVE_WIN32)
579 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
580 (void)getcwd(s + strlen(s), 256);
581 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
582 QueryPerformanceCounter(&li);
583 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
584 GetSystemTimeAsFileTime(&ft);
585 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
586 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
588 GetComputerName(s + strlen(s), &length);
590 GetUserName(s + strlen(s), &length);
593 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
594 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
595 (void)getcwd(s + strlen(s), 256);
596 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
597 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
599 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
601 #if defined(HAVE_GETHOSTID)
602 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
604 gethostname(s + strlen(s), 256);
605 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
606 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
609 MD5Update(&md5c, (uint8_t *)s, strlen(s));
610 MD5Final(md5key, &md5c);
611 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
613 MD5Update(&md5c, (uint8_t *)s, strlen(s));
614 MD5Final(md5key1, &md5c);
615 #define nextrand (md5key[j] ^ md5key1[j])
617 for (j = k = 0; j < 16; j++) {
618 unsigned char rb = nextrand;
620 #define Rad16(x) ((x) + 'A')
621 key[k++] = Rad16((rb >> 4) & 0xF);
622 key[k++] = Rad16(rb & 0xF);
630 for (j = 0; j < 16; j++) {
637 void encode_session_key(char *encode, char *session, char *key, int maxlen)
640 for (i=0; (i < maxlen-1) && session[i]; i++) {
641 if (session[i] == '-') {
644 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
648 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
651 void decode_session_key(char *decode, char *session, char *key, int maxlen)
655 for (i=0; (i < maxlen-1) && session[i]; i++) {
656 if (session[i] == '-') {
659 x = (session[i] - 'A' - key[i]) & 0xF;
667 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
673 * Edit job codes into main command line
676 * %d = Director's name
681 * %n = Unadorned Job name
683 * %t = Job type (Backup, ...)
687 * omsg = edited output message
688 * imsg = input string containing edit codes (%x)
689 * to = recepients list
692 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
697 char name[MAX_NAME_LENGTH];
701 Dmsg1(200, "edit_job_codes: %s\n", imsg);
702 for (p=imsg; *p; p++) {
710 str = jcr->client_name;
716 str = my_name; /* Director's name */
720 str = job_status_to_str(jcr->JobStatus);
727 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
733 case 'j': /* Job name */
742 str = job_level_to_str(jcr->get_JobLevel());
749 bstrncpy(name, jcr->Job, sizeof(name));
750 /* There are three periods after the Job name */
751 for (i=0; i<3; i++) {
752 if ((q=strrchr(name, '.')) != NULL) {
764 case 's': /* since time */
765 if (jcr && jcr->stime) {
773 str = job_type_to_str(jcr->get_JobType());
780 if (jcr->VolumeName && jcr->VolumeName[0]) {
781 str = jcr->VolumeName;
791 if (callback != NULL) {
792 str = callback(jcr, p);
808 Dmsg1(1200, "add_str %s\n", str);
809 pm_strcat(&omsg, str);
810 Dmsg1(1200, "omsg=%s\n", omsg);
815 void set_working_directory(char *wd)
817 struct stat stat_buf;
820 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
822 if (stat(wd, &stat_buf) != 0) {
823 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
826 if (!S_ISDIR(stat_buf.st_mode)) {
827 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
830 working_directory = wd; /* set global */
833 const char *last_path_separator(const char *str)
836 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
837 if (IsPathSeparator(*p)) {