2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 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(utime_t utime, char *buf)
143 #if defined(HAVE_WIN32)
145 * Avoid a seg fault in Microsoft's CRT localtime_r(),
146 * which incorrectly references a NULL returned from gmtime() if
147 * time is negative before or after the timezone adjustment.
151 if ((gtm = gmtime(&time)) == NULL) {
155 if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
160 if (localtime_r(&time, &tm)) {
161 n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
162 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
163 tm.tm_hour, tm.tm_min, tm.tm_sec);
171 * Convert a JobStatus code into a human readable form
173 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
180 jobstat = _("Created");
183 jobstat = _("Running");
186 jobstat = _("Blocked");
192 case JS_ErrorTerminated:
193 jobstat = _("Error");
196 jobstat = _("Non-fatal error");
199 jobstat = _("OK -- with warnings");
202 jobstat = _("Canceled");
205 jobstat = _("Verify differences");
208 jobstat = _("Waiting on FD");
211 jobstat = _("Wait on SD");
214 jobstat = _("Wait for new Volume");
217 jobstat = _("Waiting for mount");
219 case JS_WaitStoreRes:
220 jobstat = _("Waiting for Storage resource");
223 jobstat = _("Waiting for Job resource");
225 case JS_WaitClientRes:
226 jobstat = _("Waiting for Client resource");
229 jobstat = _("Waiting on Max Jobs");
231 case JS_WaitStartTime:
232 jobstat = _("Waiting for Start Time");
234 case JS_WaitPriority:
235 jobstat = _("Waiting on Priority");
237 case JS_DataCommitting:
238 jobstat = _("SD committing Data");
240 case JS_DataDespooling:
241 jobstat = _("SD despooling Data");
243 case JS_AttrDespooling:
244 jobstat = _("SD despooling Attributes");
246 case JS_AttrInserting:
247 jobstat = _("Dir inserting Attributes");
251 if (JobStatus == 0) {
254 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
259 bstrncpy(msg, jobstat, maxlen);
263 * Convert a JobStatus code into a human readable form - gui version
265 void jobstatus_to_ascii_gui(int JobStatus, char *msg, int maxlen)
267 const char *cnv = NULL;
270 cnv = _("Completed successfully");
273 cnv = _("Completed with warnings");
275 case JS_ErrorTerminated:
276 cnv = _("Terminated with errors");
279 cnv = _("Fatal error");
282 cnv = _("Created, not yet running");
285 cnv = _("Canceled by user");
288 cnv = _("Verify found differences");
291 cnv = _("Waiting for File daemon");
294 cnv = _("Waiting for Storage daemon");
296 case JS_WaitPriority:
297 cnv = _("Waiting for higher priority jobs");
299 case JS_AttrInserting:
300 cnv = _("Batch inserting file records");
305 bstrncpy(msg, cnv, maxlen);
307 jobstatus_to_ascii(JobStatus, msg, maxlen);
313 * Convert Job Termination Status into a string
315 const char *job_status_to_str(int stat)
324 str = _("OK -- with warnings");
326 case JS_ErrorTerminated:
331 str = _("Fatal Error");
337 str = _("Differences");
340 str = _("Unknown term code");
348 * Convert Job Type into a string
350 const char *job_type_to_str(int type)
380 str = _("System or Console");
386 str = _("Unknown Type");
393 * Convert Job Level into a string
395 const char *job_level_to_str(int level)
406 str = _("Incremental");
409 str = _("Differential");
414 case L_VERIFY_CATALOG:
415 str = _("Verify Catalog");
418 str = _("Verify Init Catalog");
420 case L_VERIFY_VOLUME_TO_CATALOG:
421 str = _("Verify Volume to Catalog");
423 case L_VERIFY_DISK_TO_CATALOG:
424 str = _("Verify Disk to Catalog");
427 str = _("Verify Data");
430 str = _("Virtual Full");
436 str = _("Unknown Job Level");
442 const char *volume_status_to_str(const char *status)
446 NT_("Append"), _("Append"),
447 NT_("Archive"), _("Archive"),
448 NT_("Disabled"), _("Disabled"),
449 NT_("Full"), _("Full"),
450 NT_("Used"), _("Used"),
451 NT_("Cleaning"), _("Cleaning"),
452 NT_("Purged"), _("Purged"),
453 NT_("Recycle"), _("Recycle"),
454 NT_("Read-Only"), _("Read-Only"),
455 NT_("Error"), _("Error"),
459 for (pos = 0 ; vs[pos] ; pos += 2) {
460 if ( !strcmp(vs[pos],status) ) {
466 return _("Invalid volume status");
470 /***********************************************************************
471 * Encode the mode bits into a 10 character string like LS does
472 ***********************************************************************/
474 char *encode_mode(mode_t mode, char *buf)
478 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
479 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
480 *cp++ = mode & S_IRUSR ? 'r' : '-';
481 *cp++ = mode & S_IWUSR ? 'w' : '-';
482 *cp++ = (mode & S_ISUID
483 ? (mode & S_IXUSR ? 's' : 'S')
484 : (mode & S_IXUSR ? 'x' : '-'));
485 *cp++ = mode & S_IRGRP ? 'r' : '-';
486 *cp++ = mode & S_IWGRP ? 'w' : '-';
487 *cp++ = (mode & S_ISGID
488 ? (mode & S_IXGRP ? 's' : 'S')
489 : (mode & S_IXGRP ? 'x' : '-'));
490 *cp++ = mode & S_IROTH ? 'r' : '-';
491 *cp++ = mode & S_IWOTH ? 'w' : '-';
492 *cp++ = (mode & S_ISVTX
493 ? (mode & S_IXOTH ? 't' : 'T')
494 : (mode & S_IXOTH ? 'x' : '-'));
499 #if defined(HAVE_WIN32)
500 int do_shell_expansion(char *name, int name_len)
502 char *src = bstrdup(name);
504 ExpandEnvironmentStrings(src, name, name_len);
511 int do_shell_expansion(char *name, int name_len)
513 static char meta[] = "~\\$[]*?`'<>\"";
518 char line[MAXSTRING];
519 const char *shellcmd;
521 /* Check if any meta characters are present */
523 for (i = 0; i < len; i++) {
524 if (strchr(name, meta[i])) {
530 cmd = get_pool_memory(PM_FNAME);
532 if ((shellcmd = getenv("SHELL")) == NULL) {
533 shellcmd = "/bin/sh";
535 pm_strcpy(&cmd, shellcmd);
536 pm_strcat(&cmd, " -c \"echo ");
537 pm_strcat(&cmd, name);
538 pm_strcat(&cmd, "\"");
539 Dmsg1(400, "Send: %s\n", cmd);
540 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
542 fgets(line, sizeof(line), bpipe->rfd);
543 strip_trailing_junk(line);
544 stat = close_bpipe(bpipe);
545 Dmsg2(400, "stat=%d got: %s\n", stat, line);
547 stat = 1; /* error */
549 free_pool_memory(cmd);
551 bstrncpy(name, line, name_len);
559 /* MAKESESSIONKEY -- Generate session key with optional start
560 key. If mode is TRUE, the key will be
561 translated to a string, otherwise it is
562 returned as 16 binary bytes.
564 from SpeakFreely by John Walker */
566 void make_session_key(char *key, char *seed, int mode)
569 struct MD5Context md5c;
570 unsigned char md5key[16], md5key1[16];
577 bstrncat(s, seed, sizeof(s));
580 /* The following creates a seed for the session key generator
581 based on a collection of volatile and environment-specific
582 information unlikely to be vulnerable (as a whole) to an
583 exhaustive search attack. If one of these items isn't
584 available on your machine, replace it with something
585 equivalent or, if you like, just delete it. */
587 #if defined(HAVE_WIN32)
595 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
596 (void)getcwd(s + strlen(s), 256);
597 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
598 QueryPerformanceCounter(&li);
599 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
600 GetSystemTimeAsFileTime(&ft);
601 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
602 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
604 GetComputerName(s + strlen(s), &length);
606 GetUserName(s + strlen(s), &length);
609 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
610 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
611 (void)getcwd(s + strlen(s), 256);
612 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
613 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
615 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
617 #if defined(HAVE_GETHOSTID)
618 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
620 gethostname(s + strlen(s), 256);
621 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
622 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
625 MD5Update(&md5c, (uint8_t *)s, strlen(s));
626 MD5Final(md5key, &md5c);
627 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
629 MD5Update(&md5c, (uint8_t *)s, strlen(s));
630 MD5Final(md5key1, &md5c);
631 #define nextrand (md5key[j] ^ md5key1[j])
633 for (j = k = 0; j < 16; j++) {
634 unsigned char rb = nextrand;
636 #define Rad16(x) ((x) + 'A')
637 key[k++] = Rad16((rb >> 4) & 0xF);
638 key[k++] = Rad16(rb & 0xF);
646 for (j = 0; j < 16; j++) {
653 void encode_session_key(char *encode, char *session, char *key, int maxlen)
656 for (i=0; (i < maxlen-1) && session[i]; i++) {
657 if (session[i] == '-') {
660 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
664 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
667 void decode_session_key(char *decode, char *session, char *key, int maxlen)
671 for (i=0; (i < maxlen-1) && session[i]; i++) {
672 if (session[i] == '-') {
675 x = (session[i] - 'A' - key[i]) & 0xF;
683 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
689 * Edit job codes into main command line
692 * %d = Director's name
697 * %n = Unadorned Job name
699 * %t = Job type (Backup, ...)
703 * omsg = edited output message
704 * imsg = input string containing edit codes (%x)
705 * to = recepients list
708 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
713 char name[MAX_NAME_LENGTH];
717 Dmsg1(200, "edit_job_codes: %s\n", imsg);
718 for (p=imsg; *p; p++) {
726 str = jcr->client_name;
732 str = my_name; /* Director's name */
736 str = job_status_to_str(jcr->JobStatus);
743 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
749 case 'j': /* Job name */
758 str = job_level_to_str(jcr->get_JobLevel());
765 bstrncpy(name, jcr->Job, sizeof(name));
766 /* There are three periods after the Job name */
767 for (i=0; i<3; i++) {
768 if ((q=strrchr(name, '.')) != NULL) {
780 case 's': /* since time */
781 if (jcr && jcr->stime) {
789 str = job_type_to_str(jcr->get_JobType());
796 if (jcr->VolumeName && jcr->VolumeName[0]) {
797 str = jcr->VolumeName;
807 if (callback != NULL) {
808 str = callback(jcr, p);
824 Dmsg1(1200, "add_str %s\n", str);
825 pm_strcat(&omsg, str);
826 Dmsg1(1200, "omsg=%s\n", omsg);
831 void set_working_directory(char *wd)
833 struct stat stat_buf;
836 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
838 if (stat(wd, &stat_buf) != 0) {
839 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
842 if (!S_ISDIR(stat_buf.st_mode)) {
843 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
846 working_directory = wd; /* set global */
849 const char *last_path_separator(const char *str)
852 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
853 if (IsPathSeparator(*p)) {