wrapper.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. /*
  2. * Written by Solar Designer <solar at openwall.com> in 2000-2014.
  3. * No copyright is claimed, and the software is hereby placed in the public
  4. * domain. In case this attempt to disclaim copyright and place the software
  5. * in the public domain is deemed null and void, then the software is
  6. * Copyright (c) 2000-2014 Solar Designer and it is hereby released to the
  7. * general public under the following terms:
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted.
  11. *
  12. * There's ABSOLUTELY NO WARRANTY, express or implied.
  13. *
  14. * See crypt_blowfish.c for more information.
  15. */
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <errno.h>
  19. #ifndef __set_errno
  20. #define __set_errno(val) errno = (val)
  21. #endif
  22. #ifdef TEST
  23. #include <stdio.h>
  24. #include <unistd.h>
  25. #include <signal.h>
  26. #include <time.h>
  27. #include <sys/time.h>
  28. #include <sys/times.h>
  29. #ifdef TEST_THREADS
  30. #include <pthread.h>
  31. #endif
  32. #endif
  33. #define CRYPT_OUTPUT_SIZE (7 + 22 + 31 + 1)
  34. #define CRYPT_GENSALT_OUTPUT_SIZE (7 + 22 + 1)
  35. #if defined(__GLIBC__) && defined(_LIBC)
  36. #define __SKIP_GNU
  37. #endif
  38. #include "ow-crypt.h"
  39. #include "crypt_blowfish.h"
  40. #include "crypt_gensalt.h"
  41. #if defined(__GLIBC__) && defined(_LIBC)
  42. /* crypt.h from glibc-crypt-2.1 will define struct crypt_data for us */
  43. #include "crypt.h"
  44. extern char *__md5_crypt_r(const char *key, const char *salt,
  45. char *buffer, int buflen);
  46. /* crypt-entry.c needs to be patched to define __des_crypt_r rather than
  47. * __crypt_r, and not define crypt_r and crypt at all */
  48. extern char *__des_crypt_r(const char *key, const char *salt,
  49. struct crypt_data *data);
  50. extern struct crypt_data _ufc_foobar;
  51. #endif
  52. static int _crypt_data_alloc(void **data, int *size, int need)
  53. {
  54. void *updated;
  55. if (*data && *size >= need) return 0;
  56. updated = realloc(*data, need);
  57. if (!updated) {
  58. #ifndef __GLIBC__
  59. /* realloc(3) on glibc sets errno, so we don't need to bother */
  60. __set_errno(ENOMEM);
  61. #endif
  62. return -1;
  63. }
  64. #if defined(__GLIBC__) && defined(_LIBC)
  65. if (need >= sizeof(struct crypt_data))
  66. ((struct crypt_data *)updated)->initialized = 0;
  67. #endif
  68. *data = updated;
  69. *size = need;
  70. return 0;
  71. }
  72. static char *_crypt_retval_magic(char *retval, const char *setting,
  73. char *output, int size)
  74. {
  75. if (retval)
  76. return retval;
  77. if (_crypt_output_magic(setting, output, size))
  78. return NULL; /* shouldn't happen */
  79. return output;
  80. }
  81. #if defined(__GLIBC__) && defined(_LIBC)
  82. /*
  83. * Applications may re-use the same instance of struct crypt_data without
  84. * resetting the initialized field in order to let crypt_r() skip some of
  85. * its initialization code. Thus, it is important that our multiple hashing
  86. * algorithms either don't conflict with each other in their use of the
  87. * data area or reset the initialized field themselves whenever required.
  88. * Currently, the hashing algorithms simply have no conflicts: the first
  89. * field of struct crypt_data is the 128-byte large DES key schedule which
  90. * __des_crypt_r() calculates each time it is called while the two other
  91. * hashing algorithms use less than 128 bytes of the data area.
  92. */
  93. char *__crypt_rn(__const char *key, __const char *setting,
  94. void *data, int size)
  95. {
  96. if (setting[0] == '$' && setting[1] == '2')
  97. return _crypt_blowfish_rn(key, setting, (char *)data, size);
  98. if (setting[0] == '$' && setting[1] == '1')
  99. return __md5_crypt_r(key, setting, (char *)data, size);
  100. if (setting[0] == '$' || setting[0] == '_') {
  101. __set_errno(EINVAL);
  102. return NULL;
  103. }
  104. if (size >= sizeof(struct crypt_data))
  105. return __des_crypt_r(key, setting, (struct crypt_data *)data);
  106. __set_errno(ERANGE);
  107. return NULL;
  108. }
  109. char *__crypt_ra(__const char *key, __const char *setting,
  110. void **data, int *size)
  111. {
  112. if (setting[0] == '$' && setting[1] == '2') {
  113. if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
  114. return NULL;
  115. return _crypt_blowfish_rn(key, setting, (char *)*data, *size);
  116. }
  117. if (setting[0] == '$' && setting[1] == '1') {
  118. if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
  119. return NULL;
  120. return __md5_crypt_r(key, setting, (char *)*data, *size);
  121. }
  122. if (setting[0] == '$' || setting[0] == '_') {
  123. __set_errno(EINVAL);
  124. return NULL;
  125. }
  126. if (_crypt_data_alloc(data, size, sizeof(struct crypt_data)))
  127. return NULL;
  128. return __des_crypt_r(key, setting, (struct crypt_data *)*data);
  129. }
  130. char *__crypt_r(__const char *key, __const char *setting,
  131. struct crypt_data *data)
  132. {
  133. return _crypt_retval_magic(
  134. __crypt_rn(key, setting, data, sizeof(*data)),
  135. setting, (char *)data, sizeof(*data));
  136. }
  137. char *__crypt(__const char *key, __const char *setting)
  138. {
  139. return _crypt_retval_magic(
  140. __crypt_rn(key, setting, &_ufc_foobar, sizeof(_ufc_foobar)),
  141. setting, (char *)&_ufc_foobar, sizeof(_ufc_foobar));
  142. }
  143. #else
  144. char *crypt_rn(const char *key, const char *setting, void *data, int size)
  145. {
  146. return _crypt_blowfish_rn(key, setting, (char *)data, size);
  147. }
  148. char *crypt_ra(const char *key, const char *setting,
  149. void **data, int *size)
  150. {
  151. if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
  152. return NULL;
  153. return _crypt_blowfish_rn(key, setting, (char *)*data, *size);
  154. }
  155. char *crypt_r(const char *key, const char *setting, void *data)
  156. {
  157. return _crypt_retval_magic(
  158. crypt_rn(key, setting, data, CRYPT_OUTPUT_SIZE),
  159. setting, (char *)data, CRYPT_OUTPUT_SIZE);
  160. }
  161. char *crypt(const char *key, const char *setting)
  162. {
  163. static char output[CRYPT_OUTPUT_SIZE];
  164. return _crypt_retval_magic(
  165. crypt_rn(key, setting, output, sizeof(output)),
  166. setting, output, sizeof(output));
  167. }
  168. #define __crypt_gensalt_rn crypt_gensalt_rn
  169. #define __crypt_gensalt_ra crypt_gensalt_ra
  170. #define __crypt_gensalt crypt_gensalt
  171. #endif
  172. char *__crypt_gensalt_rn(const char *prefix, unsigned long count,
  173. const char *input, int size, char *output, int output_size)
  174. {
  175. char *(*use)(const char *_prefix, unsigned long _count,
  176. const char *_input, int _size,
  177. char *_output, int _output_size);
  178. /* This may be supported on some platforms in the future */
  179. if (!input) {
  180. __set_errno(EINVAL);
  181. return NULL;
  182. }
  183. if (!strncmp(prefix, "$2a$", 4) || !strncmp(prefix, "$2b$", 4) ||
  184. !strncmp(prefix, "$2y$", 4))
  185. use = _crypt_gensalt_blowfish_rn;
  186. else
  187. if (!strncmp(prefix, "$1$", 3))
  188. use = _crypt_gensalt_md5_rn;
  189. else
  190. if (prefix[0] == '_')
  191. use = _crypt_gensalt_extended_rn;
  192. else
  193. if (!prefix[0] ||
  194. (prefix[0] && prefix[1] &&
  195. memchr(_crypt_itoa64, prefix[0], 64) &&
  196. memchr(_crypt_itoa64, prefix[1], 64)))
  197. use = _crypt_gensalt_traditional_rn;
  198. else {
  199. __set_errno(EINVAL);
  200. return NULL;
  201. }
  202. return use(prefix, count, input, size, output, output_size);
  203. }
  204. char *__crypt_gensalt_ra(const char *prefix, unsigned long count,
  205. const char *input, int size)
  206. {
  207. char output[CRYPT_GENSALT_OUTPUT_SIZE];
  208. char *retval;
  209. retval = __crypt_gensalt_rn(prefix, count,
  210. input, size, output, sizeof(output));
  211. if (retval) {
  212. retval = strdup(retval);
  213. #ifndef __GLIBC__
  214. /* strdup(3) on glibc sets errno, so we don't need to bother */
  215. if (!retval)
  216. __set_errno(ENOMEM);
  217. #endif
  218. }
  219. return retval;
  220. }
  221. char *__crypt_gensalt(const char *prefix, unsigned long count,
  222. const char *input, int size)
  223. {
  224. static char output[CRYPT_GENSALT_OUTPUT_SIZE];
  225. return __crypt_gensalt_rn(prefix, count,
  226. input, size, output, sizeof(output));
  227. }
  228. #if defined(__GLIBC__) && defined(_LIBC)
  229. weak_alias(__crypt_rn, crypt_rn)
  230. weak_alias(__crypt_ra, crypt_ra)
  231. weak_alias(__crypt_r, crypt_r)
  232. weak_alias(__crypt, crypt)
  233. weak_alias(__crypt_gensalt_rn, crypt_gensalt_rn)
  234. weak_alias(__crypt_gensalt_ra, crypt_gensalt_ra)
  235. weak_alias(__crypt_gensalt, crypt_gensalt)
  236. weak_alias(crypt, fcrypt)
  237. #endif
  238. #ifdef TEST
  239. static const char *tests[][3] = {
  240. {"$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW",
  241. "U*U"},
  242. {"$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK",
  243. "U*U*"},
  244. {"$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a",
  245. "U*U*U"},
  246. {"$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui",
  247. "0123456789abcdefghijklmnopqrstuvwxyz"
  248. "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  249. "chars after 72 are ignored"},
  250. {"$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
  251. "\xa3"},
  252. {"$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
  253. "\xff\xff\xa3"},
  254. {"$2y$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
  255. "\xff\xff\xa3"},
  256. {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.nqd1wy.pTMdcvrRWxyiGL2eMz.2a85.",
  257. "\xff\xff\xa3"},
  258. {"$2b$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
  259. "\xff\xff\xa3"},
  260. {"$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
  261. "\xa3"},
  262. {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
  263. "\xa3"},
  264. {"$2b$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
  265. "\xa3"},
  266. {"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
  267. "1\xa3" "345"},
  268. {"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
  269. "\xff\xa3" "345"},
  270. {"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
  271. "\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
  272. {"$2y$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
  273. "\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
  274. {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.ZC1JEJ8Z4gPfpe1JOr/oyPXTWl9EFd.",
  275. "\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
  276. {"$2y$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
  277. "\xff\xa3" "345"},
  278. {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
  279. "\xff\xa3" "345"},
  280. {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
  281. "\xa3" "ab"},
  282. {"$2x$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
  283. "\xa3" "ab"},
  284. {"$2y$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
  285. "\xa3" "ab"},
  286. {"$2x$05$6bNw2HLQYeqHYyBfLMsv/OiwqTymGIGzFsA4hOTWebfehXHNprcAS",
  287. "\xd1\x91"},
  288. {"$2x$05$6bNw2HLQYeqHYyBfLMsv/O9LIGgn8OMzuDoHfof8AQimSGfcSWxnS",
  289. "\xd0\xc1\xd2\xcf\xcc\xd8"},
  290. {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6",
  291. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
  292. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
  293. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
  294. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
  295. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
  296. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
  297. "chars after 72 are ignored as usual"},
  298. {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy",
  299. "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
  300. "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
  301. "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
  302. "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
  303. "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
  304. "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"},
  305. {"$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe",
  306. "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
  307. "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
  308. "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
  309. "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
  310. "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
  311. "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"},
  312. {"$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy",
  313. ""},
  314. {"*0", "", "$2a$03$CCCCCCCCCCCCCCCCCCCCC."},
  315. {"*0", "", "$2a$32$CCCCCCCCCCCCCCCCCCCCC."},
  316. {"*0", "", "$2c$05$CCCCCCCCCCCCCCCCCCCCC."},
  317. {"*0", "", "$2z$05$CCCCCCCCCCCCCCCCCCCCC."},
  318. {"*0", "", "$2`$05$CCCCCCCCCCCCCCCCCCCCC."},
  319. {"*0", "", "$2{$05$CCCCCCCCCCCCCCCCCCCCC."},
  320. {"*1", "", "*0"},
  321. {NULL}
  322. };
  323. #define which tests[0]
  324. static volatile sig_atomic_t running;
  325. static void handle_timer(int signum)
  326. {
  327. (void) signum;
  328. running = 0;
  329. }
  330. static void *run(void *arg)
  331. {
  332. unsigned long count = 0;
  333. int i = 0;
  334. void *data = NULL;
  335. int size = 0x12345678;
  336. do {
  337. const char *hash = tests[i][0];
  338. const char *key = tests[i][1];
  339. const char *setting = tests[i][2];
  340. if (!tests[++i][0])
  341. i = 0;
  342. if (setting && strlen(hash) < 30) /* not for benchmark */
  343. continue;
  344. if (strcmp(crypt_ra(key, hash, &data, &size), hash)) {
  345. printf("%d: FAILED (crypt_ra/%d/%lu)\n",
  346. (int)((char *)arg - (char *)0), i, count);
  347. free(data);
  348. return NULL;
  349. }
  350. count++;
  351. } while (running);
  352. free(data);
  353. return count + (char *)0;
  354. }
  355. int main(void)
  356. {
  357. struct itimerval it;
  358. struct tms buf;
  359. clock_t clk_tck, start_real, start_virtual, end_real, end_virtual;
  360. unsigned long count;
  361. void *data;
  362. int size;
  363. char *setting1, *setting2;
  364. int i;
  365. #ifdef TEST_THREADS
  366. pthread_t t[TEST_THREADS];
  367. void *t_retval;
  368. #endif
  369. data = NULL;
  370. size = 0x12345678;
  371. for (i = 0; tests[i][0]; i++) {
  372. const char *hash = tests[i][0];
  373. const char *key = tests[i][1];
  374. const char *setting = tests[i][2];
  375. const char *p;
  376. int ok = !setting || strlen(hash) >= 30;
  377. int o_size;
  378. char s_buf[30], o_buf[61];
  379. if (!setting) {
  380. memcpy(s_buf, hash, sizeof(s_buf) - 1);
  381. s_buf[sizeof(s_buf) - 1] = 0;
  382. setting = s_buf;
  383. }
  384. __set_errno(0);
  385. p = crypt(key, setting);
  386. if ((!ok && !errno) || strcmp(p, hash)) {
  387. printf("FAILED (crypt/%d)\n", i);
  388. return 1;
  389. }
  390. if (ok && strcmp(crypt(key, hash), hash)) {
  391. printf("FAILED (crypt/%d)\n", i);
  392. return 1;
  393. }
  394. for (o_size = -1; o_size <= (int)sizeof(o_buf); o_size++) {
  395. int ok_n = ok && o_size == (int)sizeof(o_buf);
  396. const char *x = "abc";
  397. strcpy(o_buf, x);
  398. if (o_size >= 3) {
  399. x = "*0";
  400. if (setting[0] == '*' && setting[1] == '0')
  401. x = "*1";
  402. }
  403. __set_errno(0);
  404. p = crypt_rn(key, setting, o_buf, o_size);
  405. if ((ok_n && (!p || strcmp(p, hash))) ||
  406. (!ok_n && (!errno || p || strcmp(o_buf, x)))) {
  407. printf("FAILED (crypt_rn/%d)\n", i);
  408. return 1;
  409. }
  410. }
  411. __set_errno(0);
  412. p = crypt_ra(key, setting, &data, &size);
  413. if ((ok && (!p || strcmp(p, hash))) ||
  414. (!ok && (!errno || p || strcmp((char *)data, hash)))) {
  415. printf("FAILED (crypt_ra/%d)\n", i);
  416. return 1;
  417. }
  418. }
  419. setting1 = crypt_gensalt(which[0], 12, data, size);
  420. if (!setting1 || strncmp(setting1, "$2a$12$", 7)) {
  421. puts("FAILED (crypt_gensalt)\n");
  422. return 1;
  423. }
  424. setting2 = crypt_gensalt_ra(setting1, 12, data, size);
  425. if (strcmp(setting1, setting2)) {
  426. puts("FAILED (crypt_gensalt_ra/1)\n");
  427. return 1;
  428. }
  429. (*(char *)data)++;
  430. setting1 = crypt_gensalt_ra(setting2, 12, data, size);
  431. if (!strcmp(setting1, setting2)) {
  432. puts("FAILED (crypt_gensalt_ra/2)\n");
  433. return 1;
  434. }
  435. free(setting1);
  436. free(setting2);
  437. free(data);
  438. #if defined(_SC_CLK_TCK) || !defined(CLK_TCK)
  439. clk_tck = sysconf(_SC_CLK_TCK);
  440. #else
  441. clk_tck = CLK_TCK;
  442. #endif
  443. running = 1;
  444. signal(SIGALRM, handle_timer);
  445. memset(&it, 0, sizeof(it));
  446. it.it_value.tv_sec = 5;
  447. setitimer(ITIMER_REAL, &it, NULL);
  448. start_real = times(&buf);
  449. start_virtual = buf.tms_utime + buf.tms_stime;
  450. count = (char *)run((char *)0) - (char *)0;
  451. end_real = times(&buf);
  452. end_virtual = buf.tms_utime + buf.tms_stime;
  453. if (end_virtual == start_virtual) end_virtual++;
  454. printf("%.1f c/s real, %.1f c/s virtual\n",
  455. (float)count * clk_tck / (end_real - start_real),
  456. (float)count * clk_tck / (end_virtual - start_virtual));
  457. #ifdef TEST_THREADS
  458. running = 1;
  459. it.it_value.tv_sec = 60;
  460. setitimer(ITIMER_REAL, &it, NULL);
  461. start_real = times(&buf);
  462. for (i = 0; i < TEST_THREADS; i++)
  463. if (pthread_create(&t[i], NULL, run, i + (char *)0)) {
  464. perror("pthread_create");
  465. return 1;
  466. }
  467. for (i = 0; i < TEST_THREADS; i++) {
  468. if (pthread_join(t[i], &t_retval)) {
  469. perror("pthread_join");
  470. continue;
  471. }
  472. if (!t_retval) continue;
  473. count = (char *)t_retval - (char *)0;
  474. end_real = times(&buf);
  475. printf("%d: %.1f c/s real\n", i,
  476. (float)count * clk_tck / (end_real - start_real));
  477. }
  478. #endif
  479. return 0;
  480. }
  481. #endif