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