00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #define _GNU_SOURCE
00025
00026 #ifdef HAVE_CONFIG_H
00027 # include <config.h>
00028 #endif
00029
00030 #include <sys/types.h>
00031 #include <ctype.h>
00032 #include <dirent.h>
00033 #include <glib.h>
00034 #include <stdlib.h>
00035 #include <stdio.h>
00036 #include <string.h>
00037 #include <sys/stat.h>
00038 #include <sys/times.h>
00039 #include <time.h>
00040 #include <unistd.h>
00041
00042 #include "guid.h"
00043 #include "md5.h"
00044 #include "gnc-trace.h"
00045
00046 # ifndef P_tmpdir
00047 # define P_tmpdir "/tmp"
00048 # endif
00049
00050
00051 #define DEBUG_GUID 0
00052 #define BLOCKSIZE 4096
00053 #define THRESHOLD (2 * BLOCKSIZE)
00054
00055
00056
00057 static gboolean guid_initialized = FALSE;
00058 static struct md5_ctx guid_context;
00059 static GMemChunk *guid_memchunk = NULL;
00060
00061
00062 static QofLogModule log_module = QOF_MOD_ENGINE;
00063
00064
00065 static void
00066 guid_memchunk_init (void)
00067 {
00068 if (!guid_memchunk)
00069 guid_memchunk = g_mem_chunk_create (GUID, 512, G_ALLOC_AND_FREE);
00070 }
00071
00072 static void
00073 guid_memchunk_shutdown (void)
00074 {
00075 if (guid_memchunk)
00076 {
00077 g_mem_chunk_destroy (guid_memchunk);
00078 guid_memchunk = NULL;
00079 }
00080 }
00081
00082 GUID *
00083 guid_malloc (void)
00084 {
00085 if (!guid_memchunk) guid_memchunk_init();
00086 return g_chunk_new (GUID, guid_memchunk);
00087 }
00088
00089 void
00090 guid_free (GUID *guid)
00091 {
00092 if (!guid)
00093 return;
00094
00095 g_chunk_free (guid, guid_memchunk);
00096 }
00097
00098
00099 const GUID *
00100 guid_null(void)
00101 {
00102 static int null_inited = 0;
00103 static GUID null_guid;
00104
00105 if (!null_inited)
00106 {
00107 int i;
00108 char *tmp = "NULLGUID.EMPTY.";
00109
00110
00111 for (i = 0; i < 16; i++)
00112 null_guid.data[i] = tmp[i];
00113
00114 null_inited = 1;
00115 }
00116
00117 return &null_guid;
00118 }
00119
00120
00121
00122
00123 static size_t
00124 init_from_stream(FILE *stream, size_t max_size)
00125 {
00126 char buffer[BLOCKSIZE + 72];
00127 size_t sum, block_size, total;
00128
00129 if (max_size <= 0)
00130 return 0;
00131
00132 total = 0;
00133
00134
00135 while (1)
00136 {
00137
00138
00139
00140 size_t n;
00141 sum = 0;
00142
00143 if (max_size < BLOCKSIZE)
00144 block_size = max_size;
00145 else
00146 block_size = BLOCKSIZE;
00147
00148
00149 do
00150 {
00151 n = fread (buffer + sum, 1, block_size - sum, stream);
00152
00153 sum += n;
00154 }
00155 while (sum < block_size && n != 0);
00156
00157 max_size -= sum;
00158
00159 if (n == 0 && ferror (stream))
00160 return total;
00161
00162
00163 if ((n == 0) || (max_size == 0))
00164 break;
00165
00166
00167
00168 md5_process_block (buffer, BLOCKSIZE, &guid_context);
00169
00170 total += sum;
00171 }
00172
00173
00174 if (sum > 0)
00175 {
00176 md5_process_bytes (buffer, sum, &guid_context);
00177 total += sum;
00178 }
00179
00180 return total;
00181 }
00182
00183 static size_t
00184 init_from_file(const char *filename, size_t max_size)
00185 {
00186 struct stat stats;
00187 size_t total = 0;
00188 size_t file_bytes;
00189 FILE *fp;
00190
00191 memset(&stats, 0, sizeof(stats));
00192 if (stat(filename, &stats) != 0)
00193 return 0;
00194
00195 md5_process_bytes(&stats, sizeof(stats), &guid_context);
00196 total += sizeof(stats);
00197
00198 if (max_size <= 0)
00199 return total;
00200
00201 fp = fopen (filename, "r");
00202 if (fp == NULL)
00203 return total;
00204
00205 file_bytes = init_from_stream(fp, max_size);
00206
00207 PINFO ("guid_init got %llu bytes from %s", (unsigned long long int) file_bytes,
00208 filename);
00209
00210 total += file_bytes;
00211
00212 fclose(fp);
00213
00214 return total;
00215 }
00216
00217 static size_t
00218 init_from_dir(const char *dirname, unsigned int max_files)
00219 {
00220 char filename[1024];
00221 struct dirent *de;
00222 struct stat stats;
00223 size_t total;
00224 int result;
00225 DIR *dir;
00226
00227 if (max_files <= 0)
00228 return 0;
00229
00230 dir = opendir (dirname);
00231 if (dir == NULL)
00232 return 0;
00233
00234 total = 0;
00235
00236 do
00237 {
00238 de = readdir(dir);
00239 if (de == NULL)
00240 break;
00241
00242 md5_process_bytes(de, sizeof(struct dirent), &guid_context);
00243 total += sizeof(struct dirent);
00244
00245 result = snprintf(filename, sizeof(filename),
00246 "%s/%s", dirname, de->d_name);
00247 if ((result < 0) || (result >= (int)sizeof(filename)))
00248 continue;
00249
00250 memset(&stats, 0, sizeof(stats));
00251 if (stat(filename, &stats) != 0)
00252 continue;
00253 md5_process_bytes(&stats, sizeof(stats), &guid_context);
00254 total += sizeof(stats);
00255
00256 max_files--;
00257 } while (max_files > 0);
00258
00259 closedir(dir);
00260
00261 return total;
00262 }
00263
00264 static size_t
00265 init_from_time(void)
00266 {
00267 size_t total;
00268 time_t t_time;
00269 clock_t clocks;
00270 struct tms tms_buf;
00271
00272 total = 0;
00273
00274 t_time = time(NULL);
00275 md5_process_bytes(&t_time, sizeof(t_time), &guid_context);
00276 total += sizeof(t_time);
00277
00278 clocks = times(&tms_buf);
00279 md5_process_bytes(&clocks, sizeof(clocks), &guid_context);
00280 md5_process_bytes(&tms_buf, sizeof(tms_buf), &guid_context);
00281 total += sizeof(clocks) + sizeof(tms_buf);
00282
00283 return total;
00284 }
00285
00286 static size_t
00287 init_from_int(int val)
00288 {
00289 md5_process_bytes(&val, sizeof(val), &guid_context);
00290 return sizeof(int);
00291 }
00292
00293 static size_t
00294 init_from_buff(unsigned char * buf, size_t buflen)
00295 {
00296 md5_process_bytes(buf, buflen, &guid_context);
00297 return buflen;
00298 }
00299
00300 void
00301 guid_init(void)
00302 {
00303 size_t bytes = 0;
00304
00305
00306
00307
00308 md5_init_ctx(&guid_context);
00309
00310
00311 bytes += init_from_file ("/dev/urandom", 512);
00312
00313
00314 {
00315 const char * files[] =
00316 { "/etc/passwd",
00317 "/proc/loadavg",
00318 "/proc/meminfo",
00319 "/proc/net/dev",
00320 "/proc/rtc",
00321 "/proc/self/environ",
00322 "/proc/self/stat",
00323 "/proc/stat",
00324 "/proc/uptime",
00325 NULL
00326 };
00327 int i;
00328
00329 for (i = 0; files[i] != NULL; i++)
00330 bytes += init_from_file(files[i], BLOCKSIZE);
00331 }
00332
00333
00334 {
00335 const char * dirname;
00336 const char * dirs[] =
00337 {
00338 "/proc",
00339 P_tmpdir,
00340 "/var/lock",
00341 "/var/log",
00342 "/var/mail",
00343 "/var/spool/mail",
00344 "/var/run",
00345 NULL
00346 };
00347 int i;
00348
00349 for (i = 0; dirs[i] != NULL; i++)
00350 bytes += init_from_dir(dirs[i], 32);
00351
00352 dirname = getenv("HOME");
00353 if (dirname != NULL)
00354 bytes += init_from_dir(dirname, 32);
00355 }
00356
00357
00358 {
00359 pid_t pid;
00360
00361 pid = getpid();
00362 md5_process_bytes(&pid, sizeof(pid), &guid_context);
00363 bytes += sizeof(pid);
00364
00365 pid = getppid();
00366 md5_process_bytes(&pid, sizeof(pid), &guid_context);
00367 bytes += sizeof(pid);
00368 }
00369
00370
00371 {
00372 uid_t uid;
00373 gid_t gid;
00374 char *s;
00375
00376 s = getlogin();
00377 if (s != NULL)
00378 {
00379 md5_process_bytes(s, strlen(s), &guid_context);
00380 bytes += strlen(s);
00381 }
00382
00383 uid = getuid();
00384 md5_process_bytes(&uid, sizeof(uid), &guid_context);
00385 bytes += sizeof(uid);
00386
00387 gid = getgid();
00388 md5_process_bytes(&gid, sizeof(gid), &guid_context);
00389 bytes += sizeof(gid);
00390 }
00391
00392
00393 {
00394 char string[1024];
00395
00396 memset(string, 0, sizeof(string));
00397 gethostname(string, sizeof(string));
00398 md5_process_bytes(string, sizeof(string), &guid_context);
00399 bytes += sizeof(string);
00400 }
00401
00402
00403 {
00404 int n, i;
00405
00406 srand((unsigned int) time(NULL));
00407
00408 for (i = 0; i < 32; i++)
00409 {
00410 n = rand();
00411
00412 md5_process_bytes(&n, sizeof(n), &guid_context);
00413 bytes += sizeof(n);
00414 }
00415 }
00416
00417
00418 bytes += init_from_time();
00419
00420 PINFO ("got %llu bytes", (unsigned long long int) bytes);
00421
00422 if (bytes < THRESHOLD)
00423 PWARN("only got %llu bytes.\n"
00424 "The identifiers might not be very random.\n",
00425 (unsigned long long int)bytes);
00426
00427 guid_initialized = TRUE;
00428 }
00429
00430 void
00431 guid_init_with_salt(const void *salt, size_t salt_len)
00432 {
00433 guid_init();
00434
00435 md5_process_bytes(salt, salt_len, &guid_context);
00436 }
00437
00438 void
00439 guid_init_only_salt(const void *salt, size_t salt_len)
00440 {
00441 md5_init_ctx(&guid_context);
00442
00443 md5_process_bytes(salt, salt_len, &guid_context);
00444
00445 guid_initialized = TRUE;
00446 }
00447
00448 void
00449 guid_shutdown (void)
00450 {
00451 guid_memchunk_shutdown();
00452 }
00453
00454 #define GUID_PERIOD 5000
00455
00456 void
00457 guid_new(GUID *guid)
00458 {
00459 static int counter = 0;
00460 struct md5_ctx ctx;
00461
00462 if (guid == NULL)
00463 return;
00464
00465 if (!guid_initialized)
00466 guid_init();
00467
00468
00469 ctx = guid_context;
00470 md5_finish_ctx(&ctx, guid->data);
00471
00472
00473 init_from_time();
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486 init_from_int (433781*counter);
00487 init_from_buff (guid->data, 16);
00488
00489 if (counter == 0)
00490 {
00491 FILE *fp;
00492
00493 fp = fopen ("/dev/urandom", "r");
00494 if (fp == NULL)
00495 return;
00496
00497 init_from_stream(fp, 32);
00498
00499 fclose(fp);
00500
00501 counter = GUID_PERIOD;
00502 }
00503
00504 counter--;
00505 }
00506
00507 GUID
00508 guid_new_return(void)
00509 {
00510 GUID guid;
00511
00512 guid_new (&guid);
00513
00514 return guid;
00515 }
00516
00517
00518 static void
00519 encode_md5_data(const unsigned char *data, char *buffer)
00520 {
00521 size_t count;
00522
00523 for (count = 0; count < 16; count++, buffer += 2)
00524 sprintf(buffer, "%02x", data[count]);
00525 }
00526
00527
00528
00529
00530 static gboolean
00531 decode_md5_string(const char *string, unsigned char *data)
00532 {
00533 unsigned char n1, n2;
00534 size_t count = -1;
00535 char c1, c2;
00536
00537 if (NULL == data) return FALSE;
00538 if (NULL == string) goto badstring;
00539
00540 for (count = 0; count < 16; count++)
00541 {
00542
00543 if ((0==string[2*count]) || (0==string[2*count+1])) goto badstring;
00544
00545 c1 = tolower(string[2 * count]);
00546 if (!isxdigit(c1)) goto badstring;
00547
00548 c2 = tolower(string[2 * count + 1]);
00549 if (!isxdigit(c2)) goto badstring;
00550
00551 if (isdigit(c1))
00552 n1 = c1 - '0';
00553 else
00554 n1 = c1 - 'a' + 10;
00555
00556 if (isdigit(c2))
00557 n2 = c2 - '0';
00558 else
00559 n2 = c2 - 'a' + 10;
00560
00561 data[count] = (n1 << 4) | n2;
00562 }
00563 return TRUE;
00564
00565 badstring:
00566 for (count = 0; count < 16; count++)
00567 {
00568 data[count] = 0;
00569 }
00570 return FALSE;
00571 }
00572
00573
00574
00575 const char *
00576 guid_to_string(const GUID * guid)
00577 {
00578 #ifdef G_THREADS_ENABLED
00579 static GStaticPrivate guid_buffer_key = G_STATIC_PRIVATE_INIT;
00580 gchar *string;
00581
00582 string = g_static_private_get (&guid_buffer_key);
00583 if (string == NULL) {
00584 string = malloc(GUID_ENCODING_LENGTH+1);
00585 g_static_private_set (&guid_buffer_key, string, g_free);
00586 }
00587 #else
00588 static char string[64];
00589 #endif
00590
00591 encode_md5_data(guid->data, string);
00592 string[GUID_ENCODING_LENGTH] = '\0';
00593
00594 return string;
00595 }
00596
00597 char *
00598 guid_to_string_buff(const GUID * guid, char *string)
00599 {
00600 if (!string || !guid) return NULL;
00601
00602 encode_md5_data(guid->data, string);
00603
00604 string[GUID_ENCODING_LENGTH] = '\0';
00605 return &string[GUID_ENCODING_LENGTH];
00606 }
00607
00608 gboolean
00609 string_to_guid(const char * string, GUID * guid)
00610 {
00611 return decode_md5_string(string, (guid != NULL) ? guid->data : NULL);
00612 }
00613
00614 gboolean
00615 guid_equal(const GUID *guid_1, const GUID *guid_2)
00616 {
00617 if (guid_1 && guid_2)
00618 return (memcmp(guid_1, guid_2, sizeof(GUID)) == 0);
00619 else
00620 return FALSE;
00621 }
00622
00623 gint
00624 guid_compare(const GUID *guid_1, const GUID *guid_2)
00625 {
00626 if (guid_1 == guid_2)
00627 return 0;
00628
00629
00630 if (!guid_1 && guid_2)
00631 return -1;
00632
00633 if (guid_1 && !guid_2)
00634 return 1;
00635
00636 return memcmp (guid_1, guid_2, sizeof (GUID));
00637 }
00638
00639 guint
00640 guid_hash_to_guint (gconstpointer ptr)
00641 {
00642 const GUID *guid = ptr;
00643
00644 if (!guid)
00645 {
00646 PERR ("received NULL guid pointer.");
00647 return 0;
00648 }
00649
00650 if (sizeof(guint) <= sizeof(guid->data))
00651 {
00652 return (*((guint *) guid->data));
00653 }
00654 else
00655 {
00656 guint hash = 0;
00657 unsigned int i, j;
00658
00659 for (i = 0, j = 0; i < sizeof(guint); i++, j++) {
00660 if (j == 16) j = 0;
00661
00662 hash <<= 4;
00663 hash |= guid->data[j];
00664 }
00665
00666 return hash;
00667 }
00668 }
00669
00670 static gint
00671 guid_g_hash_table_equal (gconstpointer guid_a, gconstpointer guid_b)
00672 {
00673 return guid_equal (guid_a, guid_b);
00674 }
00675
00676 GHashTable *
00677 guid_hash_table_new (void)
00678 {
00679 return g_hash_table_new (guid_hash_to_guint, guid_g_hash_table_equal);
00680 }