2 * util.c miscellaneous utility subroutines for Bacula
9 Copyright (C) 2000-2006 Kern Sibbald
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 version 2 as amended with additional clauses defined in the
14 file LICENSE in the main source directory.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 the file LICENSE for additional details.
25 #include "findlib/find.h"
28 * Various Bacula Utility subroutines
32 /* Return true of buffer has all zero bytes */
33 int is_buf_zero(char *buf, int len)
37 int i, len64, done, rem;
43 /* Optimize by checking uint64_t for zero */
44 len64 = len / sizeof(uint64_t);
45 for (i=0; i < len64; i++) {
50 done = len64 * sizeof(uint64_t); /* bytes already checked */
53 for (i = 0; i < rem; i++) {
62 /* Convert a string in place to lower case */
67 *str = tolower((int)(*str));
72 /* Convert spaces to non-space character.
73 * This makes scanf of fields containing spaces easier.
76 bash_spaces(char *str)
85 /* Convert spaces to non-space character.
86 * This makes scanf of fields containing spaces easier.
89 bash_spaces(POOL_MEM &pm)
91 char *str = pm.c_str();
100 /* Convert non-space characters (0x1) back into spaces */
102 unbash_spaces(char *str)
111 /* Convert non-space characters (0x1) back into spaces */
113 unbash_spaces(POOL_MEM &pm)
115 char *str = pm.c_str();
123 char *encode_time(time_t time, char *buf)
128 #if defined(HAVE_WIN32)
130 * Avoid a seg fault in Microsoft's CRT localtime_r(),
131 * which incorrectly references a NULL returned from gmtime() if
132 * time is negative before or after the timezone adjustment.
136 if ((gtm = gmtime(&time)) == NULL) {
140 if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
145 if (localtime_r(&time, &tm)) {
146 n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
147 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
148 tm.tm_hour, tm.tm_min, tm.tm_sec);
156 * Convert a JobStatus code into a human readable form
158 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
165 jobstat = _("Created");
168 jobstat = _("Running");
171 jobstat = _("Blocked");
177 case JS_ErrorTerminated:
178 jobstat = _("Error");
181 jobstat = _("Non-fatal error");
184 jobstat = _("Canceled");
187 jobstat = _("Verify differences");
190 jobstat = _("Waiting on FD");
193 jobstat = _("Wait on SD");
196 jobstat = _("Wait for new Volume");
199 jobstat = _("Waiting for mount");
201 case JS_WaitStoreRes:
202 jobstat = _("Waiting for Storage resource");
205 jobstat = _("Waiting for Job resource");
207 case JS_WaitClientRes:
208 jobstat = _("Waiting for Client resource");
211 jobstat = _("Waiting on Max Jobs");
213 case JS_WaitStartTime:
214 jobstat = _("Waiting for Start Time");
216 case JS_WaitPriority:
217 jobstat = _("Waiting on Priority");
221 if (JobStatus == 0) {
224 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
229 bstrncpy(msg, jobstat, maxlen);
233 * Convert Job Termination Status into a string
235 const char *job_status_to_str(int stat)
243 case JS_ErrorTerminated:
248 str = _("Fatal Error");
254 str = _("Differences");
257 str = _("Unknown term code");
265 * Convert Job Type into a string
267 const char *job_type_to_str(int type)
291 str = _("Unknown Type");
298 * Convert Job Level into a string
300 const char *job_level_to_str(int level)
311 str = _("Incremental");
314 str = _("Differential");
319 case L_VERIFY_CATALOG:
320 str = _("Verify Catalog");
323 str = _("Verify Init Catalog");
325 case L_VERIFY_VOLUME_TO_CATALOG:
326 str = _("Verify Volume to Catalog");
328 case L_VERIFY_DISK_TO_CATALOG:
329 str = _("Verify Disk to Catalog");
332 str = _("Verify Data");
338 str = _("Unknown Job Level");
345 /***********************************************************************
346 * Encode the mode bits into a 10 character string like LS does
347 ***********************************************************************/
349 char *encode_mode(mode_t mode, char *buf)
353 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
354 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
355 *cp++ = mode & S_IRUSR ? 'r' : '-';
356 *cp++ = mode & S_IWUSR ? 'w' : '-';
357 *cp++ = (mode & S_ISUID
358 ? (mode & S_IXUSR ? 's' : 'S')
359 : (mode & S_IXUSR ? 'x' : '-'));
360 *cp++ = mode & S_IRGRP ? 'r' : '-';
361 *cp++ = mode & S_IWGRP ? 'w' : '-';
362 *cp++ = (mode & S_ISGID
363 ? (mode & S_IXGRP ? 's' : 'S')
364 : (mode & S_IXGRP ? 'x' : '-'));
365 *cp++ = mode & S_IROTH ? 'r' : '-';
366 *cp++ = mode & S_IWOTH ? 'w' : '-';
367 *cp++ = (mode & S_ISVTX
368 ? (mode & S_IXOTH ? 't' : 'T')
369 : (mode & S_IXOTH ? 'x' : '-'));
375 int do_shell_expansion(char *name, int name_len)
377 static char meta[] = "~\\$[]*?`'<>\"";
382 char line[MAXSTRING];
383 const char *shellcmd;
385 /* Check if any meta characters are present */
387 for (i = 0; i < len; i++) {
388 if (strchr(name, meta[i])) {
394 cmd = get_pool_memory(PM_FNAME);
396 if ((shellcmd = getenv("SHELL")) == NULL) {
397 shellcmd = "/bin/sh";
399 pm_strcpy(&cmd, shellcmd);
400 pm_strcat(&cmd, " -c \"echo ");
401 pm_strcat(&cmd, name);
402 pm_strcat(&cmd, "\"");
403 Dmsg1(400, "Send: %s\n", cmd);
404 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
406 fgets(line, sizeof(line), bpipe->rfd);
407 strip_trailing_junk(line);
408 stat = close_bpipe(bpipe);
409 Dmsg2(400, "stat=%d got: %s\n", stat, line);
411 stat = 1; /* error */
413 free_pool_memory(cmd);
415 bstrncpy(name, line, name_len);
422 /* MAKESESSIONKEY -- Generate session key with optional start
423 key. If mode is TRUE, the key will be
424 translated to a string, otherwise it is
425 returned as 16 binary bytes.
427 from SpeakFreely by John Walker */
429 void make_session_key(char *key, char *seed, int mode)
432 struct MD5Context md5c;
433 unsigned char md5key[16], md5key1[16];
438 bstrncat(s, seed, sizeof(s));
441 /* The following creates a seed for the session key generator
442 based on a collection of volatile and environment-specific
443 information unlikely to be vulnerable (as a whole) to an
444 exhaustive search attack. If one of these items isn't
445 available on your machine, replace it with something
446 equivalent or, if you like, just delete it. */
448 sprintf(s + strlen(s), "%lu", (unsigned long)getpid());
449 sprintf(s + strlen(s), "%lu", (unsigned long)getppid());
450 (void)getcwd(s + strlen(s), 256);
451 sprintf(s + strlen(s), "%lu", (unsigned long)clock());
452 sprintf(s + strlen(s), "%lu", (unsigned long)time(NULL));
454 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
456 #if defined(HAVE_GETHOSTID)
457 sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
459 gethostname(s + strlen(s), 256);
460 sprintf(s + strlen(s), "%u", (unsigned)getuid());
461 sprintf(s + strlen(s), "%u", (unsigned)getgid());
463 MD5Update(&md5c, (unsigned char *)s, strlen(s));
464 MD5Final(md5key, &md5c);
465 sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
467 MD5Update(&md5c, (unsigned char *)s, strlen(s));
468 MD5Final(md5key1, &md5c);
469 #define nextrand (md5key[j] ^ md5key1[j])
471 for (j = k = 0; j < 16; j++) {
472 unsigned char rb = nextrand;
474 #define Rad16(x) ((x) + 'A')
475 key[k++] = Rad16((rb >> 4) & 0xF);
476 key[k++] = Rad16(rb & 0xF);
484 for (j = 0; j < 16; j++) {
494 * Edit job codes into main command line
497 * %d = Director's name
502 * %n = Unadorned Job name
504 * %t = Job type (Backup, ...)
508 * omsg = edited output message
509 * imsg = input string containing edit codes (%x)
510 * to = recepients list
513 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
518 char name[MAX_NAME_LENGTH];
522 Dmsg1(200, "edit_job_codes: %s\n", imsg);
523 for (p=imsg; *p; p++) {
531 str = jcr->client_name;
537 str = my_name; /* Director's name */
541 str = job_status_to_str(jcr->JobStatus);
548 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
554 case 'j': /* Job name */
563 str = job_level_to_str(jcr->JobLevel);
570 bstrncpy(name, jcr->Job, sizeof(name));
571 /* There are three periods after the Job name */
572 for (i=0; i<3; i++) {
573 if ((q=strrchr(name, '.')) != NULL) {
585 case 's': /* since time */
586 if (jcr && jcr->stime) {
594 str = job_type_to_str(jcr->JobType);
601 if (jcr->VolumeName && jcr->VolumeName[0]) {
602 str = jcr->VolumeName;
622 Dmsg1(1200, "add_str %s\n", str);
623 pm_strcat(&omsg, str);
624 Dmsg1(1200, "omsg=%s\n", omsg);
629 void set_working_directory(char *wd)
631 struct stat stat_buf;
634 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
636 if (stat(wd, &stat_buf) != 0) {
637 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
640 if (!S_ISDIR(stat_buf.st_mode)) {
641 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
644 working_directory = wd; /* set global */