]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
5fed0e9e91a8ff08d2b89e14c1d69470e50f82fa
[bacula/bacula] / bacula / src / lib / util.c
1 /*
2  *   util.c  miscellaneous utility subroutines for Bacula
3  * 
4  *    Kern Sibbald, MM
5  *
6  *   Version $Id$
7  */
8
9 /*
10    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation; either version 2 of
15    the License, or (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20    General Public License for more details.
21
22    You should have received a copy of the GNU General Public
23    License along with this program; if not, write to the Free
24    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25    MA 02111-1307, USA.
26
27  */
28
29 #include "bacula.h"
30 #include "jcr.h"
31 #include "findlib/find.h"
32
33 /*
34  * Various Bacula Utility subroutines
35  *
36  */
37
38 /* Return true of buffer has all zero bytes */
39 int is_buf_zero(char *buf, int len)
40 {
41    uint64_t *ip = (uint64_t *)buf;
42    char *p;
43    int i, len64, done, rem;
44
45    /* Optimize by checking uint64_t for zero */
46    len64 = len >> sizeof(uint64_t);
47    for (i=0; i < len64; i++) {
48       if (ip[i] != 0) {
49          return 0;
50       }
51    }
52    done = len64 << sizeof(uint64_t);  /* bytes already checked */
53    p = buf + done;
54    rem = len - done;
55    for (i = 0; i < rem; i++) {
56       if (p[i] != 0) {
57          return 0;
58       }
59    }
60    return 1;
61 }
62
63 /*
64  * Convert a string duration to utime_t (64 bit seconds)
65  * Returns 0: if error
66            1: if OK, and value stored in value
67  */
68 int duration_to_utime(char *str, utime_t *value)
69 {
70    int i, ch, len;
71    double val;
72    static int  mod[] = {'*', 's', 'n', 'h', 'd', 'w', 'm', 'q', 'y', 0};
73    static int mult[] = {1,    1,  60, 60*60, 60*60*24, 60*60*24*7, 60*60*24*30, 
74                   60*60*24*91, 60*60*24*365};
75
76    /* Look for modifier */
77    len = strlen(str);
78    ch = str[len - 1];
79    i = 0;
80    if (B_ISALPHA(ch)) {
81       if (B_ISUPPER(ch)) {
82          ch = tolower(ch);
83       }
84       while (mod[++i] != 0) {
85          if (ch == mod[i]) {
86             len--;
87             str[len] = 0; /* strip modifier */
88             break;
89          }
90       }
91    }
92    if (mod[i] == 0 || !is_a_number(str)) {
93       return 0;
94    }
95    val = strtod(str, NULL);
96    if (errno != 0 || val < 0) {
97       return 0;
98    }
99    *value = (utime_t)(val * mult[i]);
100    return 1;
101
102 }
103
104 /*
105  * Edit a utime "duration" into ASCII
106  */
107 char *edit_utime(utime_t val, char *buf)
108 {
109    char mybuf[30];
110    static int mult[] = {60*60*24*365, 60*60*24*30, 60*60*24, 60*60, 60};
111    static char *mod[]  = {"year",  "month",  "day", "hour", "min"};
112    int i;
113    uint32_t times;
114
115    *buf = 0;
116    for (i=0; i<5; i++) {
117       times = val / mult[i];
118       if (times > 0) {
119          val = val - (utime_t)times * mult[i];
120          sprintf(mybuf, "%d %s%s ", times, mod[i], times>1?"s":"");
121          strcat(buf, mybuf);
122       }
123    }
124    if (val == 0 && strlen(buf) == 0) {     
125       strcat(buf, "0 secs");
126    } else if (val != 0) {
127       sprintf(mybuf, "%d sec%s", (uint32_t)val, val>1?"s":"");
128       strcat(buf, mybuf);
129    }
130    return buf;
131 }
132
133 /*
134  * Convert a size size in bytes to uint64_t
135  * Returns 0: if error
136            1: if OK, and value stored in value
137  */
138 int size_to_uint64(char *str, int str_len, uint64_t *rtn_value)
139 {
140    int i, ch;
141    double value;
142    int mod[]  = {'*', 'k', 'm', 'g', 0}; /* first item * not used */
143    uint64_t mult[] = {1,             /* byte */
144                       1024,          /* kilobyte */
145                       1048576,       /* megabyte */
146                       1073741824};   /* gigabyte */
147
148 #ifdef we_have_a_compiler_that_works
149    int mod[]  = {'*', 'k', 'm', 'g', 't', 0};
150    uint64_t mult[] = {1,             /* byte */
151                       1024,          /* kilobyte */
152                       1048576,       /* megabyte */
153                       1073741824,    /* gigabyte */
154                       1099511627776};/* terabyte */
155 #endif
156
157    Dmsg0(400, "Enter sized to uint64\n");
158
159    /* Look for modifier */
160    ch = str[str_len - 1];
161    i = 0;
162    if (B_ISALPHA(ch)) {
163       if (B_ISUPPER(ch)) {
164          ch = tolower(ch);
165       }
166       while (mod[++i] != 0) {
167          if (ch == mod[i]) {
168             str_len--;
169             str[str_len] = 0; /* strip modifier */
170             break;
171          }
172       }
173    }
174    if (mod[i] == 0 || !is_a_number(str)) {
175       return 0;
176    }
177    Dmsg3(400, "size str=:%s: %f i=%d\n", str, strtod(str, NULL), i);
178
179    value = (uint64_t)strtod(str, NULL);
180    Dmsg1(400, "Int value = %d\n", (int)value);
181    if (errno != 0 || value < 0) {
182       return 0;
183    }
184    *rtn_value = (uint64_t)(value * mult[i]);
185    Dmsg2(400, "Full value = %f %" lld "\n", strtod(str, NULL) * mult[i],
186        value *mult[i]);
187    return 1;
188 }
189
190 /*
191  * Check if specified string is a number or not.
192  *  Taken from SQLite, cool, thanks.
193  */
194 int is_a_number(const char *n)
195 {
196    int digit_seen = 0;
197
198    if( *n == '-' || *n == '+' ) {
199       n++;
200    }
201    while (B_ISDIGIT(*n)) {
202       digit_seen = 1;
203       n++;
204    }
205    if (digit_seen && *n == '.') {
206       n++;
207       while (B_ISDIGIT(*n)) { n++; }
208    }
209    if (digit_seen && (*n == 'e' || *n == 'E')
210        && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
211       n += 2;                         /* skip e- or e+ or e digit */
212       while (B_ISDIGIT(*n)) { n++; }
213    }
214    return digit_seen && *n==0;
215 }
216
217
218 /*
219  * Edit an integer number with commas, the supplied buffer
220  * must be at least 27 bytes long.  The incoming number
221  * is always widened to 64 bits.
222  */
223 char *edit_uint64_with_commas(uint64_t val, char *buf)
224 {
225    sprintf(buf, "%" lld, val);
226    return add_commas(buf, buf);
227 }
228
229 /*
230  * Edit an integer number, the supplied buffer
231  * must be at least 27 bytes long.  The incoming number
232  * is always widened to 64 bits.
233  */
234 char *edit_uint64(uint64_t val, char *buf)
235 {
236    sprintf(buf, "%" lld, val);
237    return buf;
238 }
239
240
241 /*
242  * Add commas to a string, which is presumably
243  * a number.  
244  */
245 char *add_commas(char *val, char *buf)
246 {
247    int len, nc;
248    char *p, *q;
249    int i;
250
251    if (val != buf) {
252       strcpy(buf, val);
253    }
254    len = strlen(buf);
255    if (len < 1) {
256       len = 1;
257    }
258    nc = (len - 1) / 3;
259    p = buf+len;
260    q = p + nc;
261    *q-- = *p--;
262    for ( ; nc; nc--) {
263       for (i=0; i < 3; i++) {
264           *q-- = *p--;
265       }
266       *q-- = ',';
267    }   
268    return buf;
269 }
270
271
272 /* Convert a string in place to lower case */
273 void lcase(char *str)
274 {
275    while (*str) {
276       if (B_ISUPPER(*str))
277          *str = tolower((int)(*str));
278        str++;
279    }
280 }
281
282 /* Convert spaces to non-space character. 
283  * This makes scanf of fields containing spaces easier.
284  */
285 void
286 bash_spaces(char *str)
287 {
288    while (*str) {
289       if (*str == ' ')
290          *str = 0x1;
291       str++;
292    }
293 }
294
295 /* Convert non-space characters (0x1) back into spaces */
296 void
297 unbash_spaces(char *str)
298 {
299    while (*str) {
300      if (*str == 0x1)
301         *str = ' ';
302      str++;
303    }
304 }
305
306 /* Strip any trailing junk from the command */
307 void strip_trailing_junk(char *cmd)
308 {
309    char *p;
310    p = cmd + strlen(cmd) - 1;
311
312    /* strip trailing junk from command */
313    while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' '))
314       *p-- = 0;
315 }
316
317 /* Strip any trailing slashes from a directory path */
318 void strip_trailing_slashes(char *dir)
319 {
320    char *p;
321    p = dir + strlen(dir) - 1;
322
323    /* strip trailing slashes */
324    while ((p >= dir) && (*p == '/'))
325       *p-- = 0;
326 }
327
328 /*
329  * Skip spaces
330  *  Returns: 0 on failure (EOF)             
331  *           1 on success
332  *           new address in passed parameter 
333  */
334 int skip_spaces(char **msg)
335 {
336    char *p = *msg;
337    if (!p) {
338       return 0;
339    }
340    while (*p && *p == ' ') {
341       p++;
342    }
343    *msg = p;
344    return *p ? 1 : 0;
345 }
346
347 /*
348  * Skip nonspaces
349  *  Returns: 0 on failure (EOF)             
350  *           1 on success
351  *           new address in passed parameter 
352  */
353 int skip_nonspaces(char **msg)
354 {
355    char *p = *msg;
356
357    if (!p) {
358       return 0;
359    }
360    while (*p && *p != ' ') {
361       p++;
362    }
363    *msg = p;
364    return *p ? 1 : 0;
365 }
366
367 /* folded search for string - case insensitive */
368 int
369 fstrsch(char *a, char *b)   /* folded case search */
370 {
371    register char *s1,*s2;
372    register char c1, c2;
373
374    s1=a;
375    s2=b;
376    while (*s1) {                      /* do it the fast way */
377       if ((*s1++ | 0x20) != (*s2++ | 0x20))
378          return 0;                    /* failed */
379    }
380    while (*a) {                       /* do it over the correct slow way */
381       if (B_ISUPPER(c1 = *a)) {
382          c1 = tolower((int)c1);
383       }
384       if (B_ISUPPER(c2 = *b)) {
385          c2 = tolower((int)c2);
386       }
387       if (c1 != c2) {
388          return 0;
389       }
390       a++;
391       b++;
392    }
393    return 1;
394 }
395
396
397 char *encode_time(time_t time, char *buf)
398 {
399    struct tm tm;
400    int n = 0;
401
402    if (localtime_r(&time, &tm)) {
403       n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
404                    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
405                    tm.tm_hour, tm.tm_min, tm.tm_sec);
406    }
407    return buf+n;
408 }
409
410 /*
411  * Concatenate a string (str) onto a pool memory buffer pm
412  */
413 void pm_strcat(POOLMEM **pm, char *str)
414 {
415    int pmlen = strlen(*pm);
416    int len = strlen(str) + 1;
417
418    *pm = check_pool_memory_size(*pm, pmlen + len);
419    memcpy(*pm+pmlen, str, len);
420 }
421
422
423 /*
424  * Copy a string (str) into a pool memory buffer pm
425  */
426 void pm_strcpy(POOLMEM **pm, char *str)
427 {
428    int len = strlen(str) + 1;
429
430    *pm = check_pool_memory_size(*pm, len);
431    memcpy(*pm, str, len);
432 }
433
434
435 /*
436  * Convert a JobStatus code into a human readable form
437  */
438 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
439 {
440    char *termstat, jstat[2];
441
442    switch (JobStatus) {
443       case JS_Terminated:
444          termstat = _("OK");
445          break;
446      case JS_FatalError:
447      case JS_ErrorTerminated:
448          termstat = _("Error");
449          break;
450      case JS_Error:
451          termstat = _("Non-fatal error");
452          break;
453      case JS_Cancelled:
454          termstat = _("Cancelled");
455          break;
456      case JS_Differences:
457          termstat = _("Verify differences");
458          break;
459      default:
460          jstat[0] = last_job.JobStatus;
461          jstat[1] = 0;
462          termstat = jstat;
463          break;
464    }
465    strncpy(msg, termstat, maxlen);
466    msg[maxlen-1] = 0;
467 }
468
469 /*
470  * Convert Job Termination Status into a string
471  */
472 char *job_status_to_str(int stat) 
473 {
474    char *str;
475
476    switch (stat) {
477    case JS_Terminated:
478       str = _("OK");
479       break;
480    case JS_ErrorTerminated:
481    case JS_Error:
482       str = _("Error");
483       break;
484    case JS_FatalError:
485       str = _("Fatal Error");
486       break;
487    case JS_Cancelled:
488       str = _("Cancelled");
489       break;
490    case JS_Differences:
491       str = _("Differences");
492       break;
493    default:
494       str = _("Unknown term code");
495       break;
496    }
497    return str;
498 }
499
500
501 /*
502  * Convert Job Type into a string
503  */
504 char *job_type_to_str(int type) 
505 {
506    char *str;
507
508    switch (type) {
509    case JT_BACKUP:
510       str = _("Backup");
511       break;
512    case JT_VERIFY:
513       str = _("Verify");
514       break;
515    case JT_RESTORE:
516       str = _("Restore");
517       break;
518    case JT_ADMIN:
519       str = _("Admin");
520       break;
521    default:
522       str = _("Unknown Type");
523       break;
524    }
525    return str;
526 }
527
528 /*
529  * Convert Job Level into a string
530  */
531 char *job_level_to_str(int level) 
532 {
533    char *str;
534
535    switch (level) {
536    case L_FULL:
537       str = _("Full");
538       break;
539    case L_INCREMENTAL:
540       str = _("Incremental");
541       break;
542    case L_DIFFERENTIAL:
543       str = _("Differential");
544       break;
545    case L_LEVEL:
546       str = _("Level");
547       break;
548    case L_SINCE:
549       str = _("Since");
550       break;
551    case L_VERIFY_CATALOG:
552       str = _("Verify Catalog");
553       break;
554    case L_VERIFY_INIT:
555       str = _("Verify Init Catalog");
556       break;
557    case L_VERIFY_VOLUME_TO_CATALOG:
558       str = _("Verify Volume to Catalog");
559       break;
560    case L_VERIFY_DATA:
561       str = _("Verify Data");
562       break;
563    default:
564       str = _("Unknown Job Level");
565       break;
566    }
567    return str;
568 }
569
570
571 /***********************************************************************
572  * Encode the mode bits into a 10 character string like LS does
573  ***********************************************************************/
574
575 char *encode_mode(mode_t mode, char *buf)
576 {
577   char *cp = buf;  
578
579   *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
580           S_ISLNK(mode) ? 'l' : '-';
581   *cp++ = mode & S_IRUSR ? 'r' : '-';
582   *cp++ = mode & S_IWUSR ? 'w' : '-';
583   *cp++ = (mode & S_ISUID
584                ? (mode & S_IXUSR ? 's' : 'S')
585                : (mode & S_IXUSR ? 'x' : '-'));
586   *cp++ = mode & S_IRGRP ? 'r' : '-';
587   *cp++ = mode & S_IWGRP ? 'w' : '-';
588   *cp++ = (mode & S_ISGID
589                ? (mode & S_IXGRP ? 's' : 'S')
590                : (mode & S_IXGRP ? 'x' : '-'));
591   *cp++ = mode & S_IROTH ? 'r' : '-';
592   *cp++ = mode & S_IWOTH ? 'w' : '-';
593   *cp++ = (mode & S_ISVTX
594                ? (mode & S_IXOTH ? 't' : 'T')
595                : (mode & S_IXOTH ? 'x' : '-'));
596   *cp = '\0';
597   return cp;
598 }
599
600
601 int do_shell_expansion(char *name)
602 {
603 /*  ****FIXME***** this should work for Win32 too */
604 #define UNIX
605 #ifdef UNIX
606 #ifndef PATH_MAX
607 #define PATH_MAX 512
608 #endif
609
610    int pid, wpid, stat;
611    int waitstatus;
612    char *shellcmd;
613    void (*istat)(int), (*qstat)(int);
614    int i;
615    char echout[PATH_MAX + 256];
616    int pfd[2];
617    static char meta[] = "~\\$[]*?`'<>\"";
618    int found = FALSE;
619    int len;
620
621    /* Check if any meta characters are present */
622    len = strlen(meta);
623    for (i = 0; i < len; i++) {
624       if (strchr(name, meta[i])) {
625          found = TRUE;
626          break;
627       }
628    }
629    stat = 0;
630    if (found) {
631 #ifdef nt
632        /* If the filename appears to be a DOS filename,
633           convert all backward slashes \ to Unix path
634           separators / and insert a \ infront of spaces. */
635        len = strlen(name);
636        if (len >= 3 && name[1] == ':' && name[2] == '\\') {
637           for (i=2; i<len; i++)
638              if (name[i] == '\\')
639                 name[i] = '/';
640        }
641 #else
642        /* Pass string off to the shell for interpretation */
643        if (pipe(pfd) == -1)
644           return 0;
645        switch(pid = fork()) {
646        case -1:
647           break;
648
649        case 0:                            /* child */
650           /* look for shell */
651           if ((shellcmd = getenv("SHELL")) == NULL)
652              shellcmd = "/bin/sh";
653           close(1); dup(pfd[1]);          /* attach pipes to stdin and stdout */
654           close(2); dup(pfd[1]);
655           for (i = 3; i < 32; i++)        /* close everything else */
656              close(i);
657           strcpy(echout, "echo ");        /* form echo command */
658           strcat(echout, name);
659           execl(shellcmd, shellcmd, "-c", echout, NULL); /* give to shell */
660           exit(127);                      /* shouldn't get here */
661
662        default:                           /* parent */
663           /* read output from child */
664           i = read(pfd[0], echout, sizeof echout);
665           echout[--i] = 0;                /* set end of string */
666           /* look for first word or first line. */
667           while (--i >= 0) {
668              if (echout[i] == ' ' || echout[i] == '\n')
669                 echout[i] = 0;            /* keep only first one */
670           }
671           istat = signal(SIGINT, SIG_IGN);
672           qstat = signal(SIGQUIT, SIG_IGN);
673           /* wait for child to exit */
674           while ((wpid = wait(&waitstatus)) != pid && wpid != -1)
675              { ; }
676           signal(SIGINT, istat);
677           signal(SIGQUIT, qstat);
678           strcpy(name, echout);
679           stat = 1;
680           break;
681        }
682        close(pfd[0]);                     /* close pipe */
683        close(pfd[1]);
684 #endif /* nt */
685    }
686    return stat;
687
688 #endif /* UNIX */
689
690 #if  MSC | MSDOS | __WATCOMC__
691
692    char prefix[100], *env, *getenv();
693
694    /* Home directory reference? */
695    if (*name == '~' && (env=getenv("HOME"))) {
696       strcpy(prefix, env);            /* copy HOME directory name */
697       name++;                         /* skip over ~ in name */
698       strcat(prefix, name);
699       name--;                         /* get back to beginning */
700       strcpy(name, prefix);           /* move back into name */
701    }
702    return 1;
703 #endif
704
705 }
706
707 #define MAX_ARGV 100
708 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg);
709
710 /*
711  * Run an external program. Optionally wait a specified number
712  *   of seconds. Program killed if wait exceeded. Optionally
713  *   return the output from the program (normally a single line).
714  */
715 int run_program(char *prog, int wait, POOLMEM *results)
716 {
717    int stat = ETIME;
718    int chldstatus = 0;
719    pid_t pid1, pid2 = 0;
720    int pfd[2];
721    char *bargv[MAX_ARGV];
722    int bargc;
723
724    
725    build_argc_argv(prog, &bargc, bargv, MAX_ARGV);
726 #ifdef xxxxxxxxxx
727    printf("argc=%d\n", bargc);
728    int i;
729    for (i=0; i<bargc; i++) {
730       printf("argc=%d argv=%s\n", i, bargv[i]);
731    }
732 #endif
733
734    if (results && pipe(pfd) == -1) {
735       return errno;
736    }
737    /* Start worker process */
738    switch (pid1 = fork()) {
739    case -1:
740       break;
741
742    case 0:                            /* child */
743 //    printf("execl of %s\n", prog);
744       if (results) {
745          close(1); dup(pfd[1]);       /* attach pipes to stdin and stdout */
746          close(2); dup(pfd[1]);
747       }
748       execvp(bargv[0], bargv);
749       exit(errno);                   /* shouldn't get here */
750
751    default:                           /* parent */
752       /* start timer process */
753       if (wait > 0) {
754          switch (pid2=fork()) {
755          case -1:
756             break;
757          case 0:                         /* child 2 */
758             /* Time the worker process */  
759             sleep(wait);
760             if (kill(pid1, SIGTERM) == 0) { /* time expired kill it */
761                exit(0);
762             }
763             sleep(3);
764             kill(pid1, SIGKILL);
765             exit(0);
766          default:                        /* parent */
767             break;
768          }
769       }
770
771       /* Parent continues here */
772       int i;
773       if (results) {
774          i = read(pfd[0], results, sizeof_pool_memory(results) - 1);
775          if (--i < 0) {
776             i = 0;
777          }
778          results[i] = 0;                /* set end of string */
779       }
780       /* wait for worker child to exit */
781       for ( ;; ) {
782          pid_t wpid;
783          wpid = waitpid(pid1, &chldstatus, 0);         
784          if (wpid == pid1 || (errno != EINTR)) {
785             break;
786          }
787       }
788       if (WIFEXITED(chldstatus))
789          stat = WEXITSTATUS(chldstatus);
790
791       if (wait > 0) {
792          kill(pid2, SIGKILL);           /* kill off timer process */
793          waitpid(pid2, &chldstatus, 0); /* reap timer process */
794       }
795       if (results) { 
796          close(pfd[0]);              /* close pipe */
797          close(pfd[1]);
798       }
799       break;
800    }
801    return stat;
802 }
803
804 /*
805  * Build argc and argv from a string
806  */
807 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_argv)
808 {
809    int i, quote;
810    char *p, *q;
811    int argc = 0;
812
813    argc = 0;
814    for (i=0; i<max_argv; i++)
815       bargv[i] = NULL;
816
817    p = cmd;
818    quote = 0;
819    while  (*p && (*p == ' ' || *p == '\t'))
820       p++;
821    if (*p == '\"') {
822       quote = 1;
823       p++;
824    }
825    if (*p) {
826       while (*p && argc < MAX_ARGV) {
827          q = p;
828          if (quote) {
829             while (*q && *q != '\"')
830             q++;
831             quote = 0;
832          } else {
833             while (*q && *q != ' ')
834             q++;
835          }
836          if (*q)
837             *(q++) = '\0';
838          bargv[argc++] = p;
839          p = q;
840          while (*p && (*p == ' ' || *p == '\t'))
841             p++;
842          if (*p == '\"') {
843             quote = 1;
844             p++;
845          }
846       }
847    }
848    *bargc = argc;
849 }
850
851 /*  MAKESESSIONKEY  --  Generate session key with optional start
852                         key.  If mode is TRUE, the key will be
853                         translated to a string, otherwise it is
854                         returned as 16 binary bytes.
855
856     from SpeakFreely by John Walker */
857
858 void makeSessionKey(char *key, char *seed, int mode)
859 {
860      int j, k;
861      struct MD5Context md5c;
862      unsigned char md5key[16], md5key1[16];
863      char s[1024];
864
865      s[0] = 0;
866      if (seed != NULL) {
867         strcat(s, seed);
868      }
869
870      /* The following creates a seed for the session key generator
871         based on a collection of volatile and environment-specific
872         information unlikely to be vulnerable (as a whole) to an
873         exhaustive search attack.  If one of these items isn't
874         available on your machine, replace it with something
875         equivalent or, if you like, just delete it. */
876
877      sprintf(s + strlen(s), "%lu", (unsigned long) getpid());
878      sprintf(s + strlen(s), "%lu", (unsigned long) getppid());
879      getcwd(s + strlen(s), 256);
880      sprintf(s + strlen(s), "%lu", (unsigned long) clock());
881      sprintf(s + strlen(s), "%lu", (unsigned long) time(NULL));
882 #ifdef Solaris
883      sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
884 #endif
885 #ifdef HAVE_GETHOSTID
886      sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
887 #endif
888 #ifdef HAVE_GETDOMAINNAME
889      getdomainname(s + strlen(s), 256);
890 #endif
891      gethostname(s + strlen(s), 256);
892      sprintf(s + strlen(s), "%u", (unsigned)getuid());
893      sprintf(s + strlen(s), "%u", (unsigned)getgid());
894      MD5Init(&md5c);
895      MD5Update(&md5c, (unsigned char *)s, strlen(s));
896      MD5Final(md5key, &md5c);
897      sprintf(s + strlen(s), "%lu", (unsigned long) ((time(NULL) + 65121) ^ 0x375F));
898      MD5Init(&md5c);
899      MD5Update(&md5c, (unsigned char *)s, strlen(s));
900      MD5Final(md5key1, &md5c);
901 #define nextrand    (md5key[j] ^ md5key1[j])
902      if (mode) {
903         for (j = k = 0; j < 16; j++) {
904             unsigned char rb = nextrand;
905
906 #define Rad16(x) ((x) + 'A')
907             key[k++] = Rad16((rb >> 4) & 0xF);
908             key[k++] = Rad16(rb & 0xF);
909 #undef Rad16
910             if (j & 1) {
911                  key[k++] = '-';
912             }
913         }
914         key[--k] = 0;
915      } else {
916         for (j = 0; j < 16; j++) {
917             key[j] = nextrand;
918         }
919      }
920 }
921 #undef nextrand