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");
417 str = _("Virtual Full");
423 str = _("Unknown Job Level");
429 const char *volume_status_to_str(const char *status)
433 NT_("Append"), _("Append"),
434 NT_("Archive"), _("Archive"),
435 NT_("Disabled"), _("Disabled"),
436 NT_("Full"), _("Full"),
437 NT_("Used"), _("Used"),
438 NT_("Cleaning"), _("Cleaning"),
439 NT_("Purged"), _("Purged"),
440 NT_("Recycle"), _("Recycle"),
441 NT_("Read-Only"), _("Read-Only"),
442 NT_("Error"), _("Error"),
446 for (pos = 0 ; vs[pos] ; pos += 2) {
447 if ( !strcmp(vs[pos],status) ) {
453 return _("Invalid volume status");
457 /***********************************************************************
458 * Encode the mode bits into a 10 character string like LS does
459 ***********************************************************************/
461 char *encode_mode(mode_t mode, char *buf)
465 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
466 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
467 *cp++ = mode & S_IRUSR ? 'r' : '-';
468 *cp++ = mode & S_IWUSR ? 'w' : '-';
469 *cp++ = (mode & S_ISUID
470 ? (mode & S_IXUSR ? 's' : 'S')
471 : (mode & S_IXUSR ? 'x' : '-'));
472 *cp++ = mode & S_IRGRP ? 'r' : '-';
473 *cp++ = mode & S_IWGRP ? 'w' : '-';
474 *cp++ = (mode & S_ISGID
475 ? (mode & S_IXGRP ? 's' : 'S')
476 : (mode & S_IXGRP ? 'x' : '-'));
477 *cp++ = mode & S_IROTH ? 'r' : '-';
478 *cp++ = mode & S_IWOTH ? 'w' : '-';
479 *cp++ = (mode & S_ISVTX
480 ? (mode & S_IXOTH ? 't' : 'T')
481 : (mode & S_IXOTH ? 'x' : '-'));
486 #if defined(HAVE_WIN32)
487 int do_shell_expansion(char *name, int name_len)
489 char *src = bstrdup(name);
491 ExpandEnvironmentStrings(src, name, name_len);
498 int do_shell_expansion(char *name, int name_len)
500 static char meta[] = "~\\$[]*?`'<>\"";
505 char line[MAXSTRING];
506 const char *shellcmd;
508 /* Check if any meta characters are present */
510 for (i = 0; i < len; i++) {
511 if (strchr(name, meta[i])) {
517 cmd = get_pool_memory(PM_FNAME);
519 if ((shellcmd = getenv("SHELL")) == NULL) {
520 shellcmd = "/bin/sh";
522 pm_strcpy(&cmd, shellcmd);
523 pm_strcat(&cmd, " -c \"echo ");
524 pm_strcat(&cmd, name);
525 pm_strcat(&cmd, "\"");
526 Dmsg1(400, "Send: %s\n", cmd);
527 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
529 fgets(line, sizeof(line), bpipe->rfd);
530 strip_trailing_junk(line);
531 stat = close_bpipe(bpipe);
532 Dmsg2(400, "stat=%d got: %s\n", stat, line);
534 stat = 1; /* error */
536 free_pool_memory(cmd);
538 bstrncpy(name, line, name_len);
546 /* MAKESESSIONKEY -- Generate session key with optional start
547 key. If mode is TRUE, the key will be
548 translated to a string, otherwise it is
549 returned as 16 binary bytes.
551 from SpeakFreely by John Walker */
553 void make_session_key(char *key, char *seed, int mode)
556 struct MD5Context md5c;
557 unsigned char md5key[16], md5key1[16];
564 bstrncat(s, seed, sizeof(s));
567 /* The following creates a seed for the session key generator
568 based on a collection of volatile and environment-specific
569 information unlikely to be vulnerable (as a whole) to an
570 exhaustive search attack. If one of these items isn't
571 available on your machine, replace it with something
572 equivalent or, if you like, just delete it. */
574 #if defined(HAVE_WIN32)
582 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
583 (void)getcwd(s + strlen(s), 256);
584 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
585 QueryPerformanceCounter(&li);
586 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
587 GetSystemTimeAsFileTime(&ft);
588 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
589 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
591 GetComputerName(s + strlen(s), &length);
593 GetUserName(s + strlen(s), &length);
596 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
597 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
598 (void)getcwd(s + strlen(s), 256);
599 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
600 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
602 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
604 #if defined(HAVE_GETHOSTID)
605 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
607 gethostname(s + strlen(s), 256);
608 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
609 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
612 MD5Update(&md5c, (uint8_t *)s, strlen(s));
613 MD5Final(md5key, &md5c);
614 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
616 MD5Update(&md5c, (uint8_t *)s, strlen(s));
617 MD5Final(md5key1, &md5c);
618 #define nextrand (md5key[j] ^ md5key1[j])
620 for (j = k = 0; j < 16; j++) {
621 unsigned char rb = nextrand;
623 #define Rad16(x) ((x) + 'A')
624 key[k++] = Rad16((rb >> 4) & 0xF);
625 key[k++] = Rad16(rb & 0xF);
633 for (j = 0; j < 16; j++) {
640 void encode_session_key(char *encode, char *session, char *key, int maxlen)
643 for (i=0; (i < maxlen-1) && session[i]; i++) {
644 if (session[i] == '-') {
647 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
651 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
654 void decode_session_key(char *decode, char *session, char *key, int maxlen)
658 for (i=0; (i < maxlen-1) && session[i]; i++) {
659 if (session[i] == '-') {
662 x = (session[i] - 'A' - key[i]) & 0xF;
670 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
676 * Edit job codes into main command line
679 * %d = Director's name
684 * %n = Unadorned Job name
686 * %t = Job type (Backup, ...)
690 * omsg = edited output message
691 * imsg = input string containing edit codes (%x)
692 * to = recepients list
695 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
700 char name[MAX_NAME_LENGTH];
704 Dmsg1(200, "edit_job_codes: %s\n", imsg);
705 for (p=imsg; *p; p++) {
713 str = jcr->client_name;
719 str = my_name; /* Director's name */
723 str = job_status_to_str(jcr->JobStatus);
730 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
736 case 'j': /* Job name */
745 str = job_level_to_str(jcr->get_JobLevel());
752 bstrncpy(name, jcr->Job, sizeof(name));
753 /* There are three periods after the Job name */
754 for (i=0; i<3; i++) {
755 if ((q=strrchr(name, '.')) != NULL) {
767 case 's': /* since time */
768 if (jcr && jcr->stime) {
776 str = job_type_to_str(jcr->get_JobType());
783 if (jcr->VolumeName && jcr->VolumeName[0]) {
784 str = jcr->VolumeName;
794 if (callback != NULL) {
795 str = callback(jcr, p);
811 Dmsg1(1200, "add_str %s\n", str);
812 pm_strcat(&omsg, str);
813 Dmsg1(1200, "omsg=%s\n", omsg);
818 void set_working_directory(char *wd)
820 struct stat stat_buf;
823 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
825 if (stat(wd, &stat_buf) != 0) {
826 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
829 if (!S_ISDIR(stat_buf.st_mode)) {
830 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
833 working_directory = wd; /* set global */
836 const char *last_path_separator(const char *str)
839 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
840 if (IsPathSeparator(*p)) {