1 /*
2 ** Copyright (c) 2006 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clause License" or "FreeBSD License".)
7
8 ** This program is distributed in the hope that it will be useful,
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Author contact information:
13 ** drh@hwaci.com
14 ** http://www.hwaci.com/drh/
15 **
16 *******************************************************************************
17 **
18 ** This file contains code for miscellaneous utility routines.
19 */
20 #include "config.h"
21 #include "util.h"
22 #if defined(USE_MMAN_H)
23 # include <sys/mman.h>
24 # include <unistd.h>
25 #endif
26 #include <math.h>
27
28 /*
29 ** For the fossil_timer_xxx() family of functions...
30 */
31 #ifdef _WIN32
32 # include <windows.h>
33 #else
34 # include <sys/time.h>
35 # include <sys/resource.h>
36 # include <sys/types.h>
37 # include <sys/stat.h>
38 # include <unistd.h>
39 # include <fcntl.h>
40 # include <errno.h>
41 #endif
42
43 /*
44 ** Exit. Take care to close the database first.
45 */
fossil_exit(int rc)46 NORETURN void fossil_exit(int rc){
47 db_close(1);
48 #ifndef _WIN32
49 if( g.fAnyTrace ){
50 fprintf(stderr, "/***** Subprocess %d exit(%d) *****/\n", getpid(), rc);
51 fflush(stderr);
52 }
53 #endif
54 exit(rc);
55 }
56
57 /*
58 ** Malloc and free routines that cannot fail
59 */
fossil_malloc(size_t n)60 void *fossil_malloc(size_t n){
61 void *p = malloc(n==0 ? 1 : n);
62 if( p==0 ) fossil_fatal("out of memory");
63 return p;
64 }
fossil_malloc_zero(size_t n)65 void *fossil_malloc_zero(size_t n){
66 void *p = malloc(n==0 ? 1 : n);
67 if( p==0 ) fossil_fatal("out of memory");
68 memset(p, 0, n);
69 return p;
70 }
fossil_free(void * p)71 void fossil_free(void *p){
72 free(p);
73 }
fossil_realloc(void * p,size_t n)74 void *fossil_realloc(void *p, size_t n){
75 p = realloc(p, n);
76 if( p==0 ) fossil_fatal("out of memory");
77 return p;
78 }
fossil_secure_zero(void * p,size_t n)79 void fossil_secure_zero(void *p, size_t n){
80 volatile unsigned char *vp = (volatile unsigned char *)p;
81 size_t i;
82
83 if( p==0 ) return;
84 assert( n>0 );
85 if( n==0 ) return;
86 for(i=0; i<n; i++){ vp[i] ^= 0xFF; }
87 for(i=0; i<n; i++){ vp[i] ^= vp[i]; }
88 }
fossil_get_page_size(size_t * piPageSize)89 void fossil_get_page_size(size_t *piPageSize){
90 #if defined(_WIN32)
91 SYSTEM_INFO sysInfo;
92 memset(&sysInfo, 0, sizeof(SYSTEM_INFO));
93 GetSystemInfo(&sysInfo);
94 *piPageSize = (size_t)sysInfo.dwPageSize;
95 #elif defined(USE_MMAN_H)
96 *piPageSize = (size_t)sysconf(_SC_PAGE_SIZE);
97 #else
98 *piPageSize = 4096; /* FIXME: What for POSIX? */
99 #endif
100 }
fossil_secure_alloc_page(size_t * pN)101 void *fossil_secure_alloc_page(size_t *pN){
102 void *p;
103 size_t pageSize = 0;
104
105 fossil_get_page_size(&pageSize);
106 assert( pageSize>0 );
107 assert( pageSize%2==0 );
108 #if defined(_WIN32)
109 p = VirtualAlloc(NULL, pageSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
110 if( p==NULL ){
111 fossil_fatal("VirtualAlloc failed: %lu\n", GetLastError());
112 }
113 if( !VirtualLock(p, pageSize) ){
114 fossil_fatal("VirtualLock failed: %lu\n", GetLastError());
115 }
116 #elif defined(USE_MMAN_H)
117 p = mmap(0, pageSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
118 if( p==MAP_FAILED ){
119 fossil_fatal("mmap failed: %d\n", errno);
120 }
121 if( mlock(p, pageSize) ){
122 fossil_fatal("mlock failed: %d\n", errno);
123 }
124 #else
125 p = fossil_malloc(pageSize);
126 #endif
127 fossil_secure_zero(p, pageSize);
128 if( pN ) *pN = pageSize;
129 return p;
130 }
fossil_secure_free_page(void * p,size_t n)131 void fossil_secure_free_page(void *p, size_t n){
132 if( !p ) return;
133 assert( n>0 );
134 fossil_secure_zero(p, n);
135 #if defined(_WIN32)
136 if( !VirtualUnlock(p, n) ){
137 fossil_panic("VirtualUnlock failed: %lu\n", GetLastError());
138 }
139 if( !VirtualFree(p, 0, MEM_RELEASE) ){
140 fossil_panic("VirtualFree failed: %lu\n", GetLastError());
141 }
142 #elif defined(USE_MMAN_H)
143 if( munlock(p, n) ){
144 fossil_panic("munlock failed: %d\n", errno);
145 }
146 if( munmap(p, n) ){
147 fossil_panic("munmap failed: %d\n", errno);
148 }
149 #else
150 fossil_free(p);
151 #endif
152 }
153
154 /*
155 ** Translate every upper-case character in the input string into
156 ** its equivalent lower-case.
157 */
fossil_strtolwr(char * zIn)158 char *fossil_strtolwr(char *zIn){
159 char *zStart = zIn;
160 if( zIn ){
161 while( *zIn ){
162 *zIn = fossil_tolower(*zIn);
163 zIn++;
164 }
165 }
166 return zStart;
167 }
168
169 /*
170 ** This local variable determines the behavior of
171 ** fossil_assert_safe_command_string():
172 **
173 ** 0 (default) fossil_panic() on an unsafe command string
174 **
175 ** 1 Print an error but continue process. Used for
176 ** testing of fossil_assert_safe_command_string().
177 **
178 ** 2 No-op. Used to allow any arbitrary command string
179 ** through fossil_system(), such as when invoking
180 ** COMMAND in "fossil bisect run COMMAND".
181 */
182 static int safeCmdStrTest = 0;
183
184 /*
185 ** Check the input string to ensure that it is safe to pass into system().
186 ** A string is unsafe for system() on unix if it contains any of the following:
187 **
188 ** * Any occurrance of '$' or '`' except single-quoted or after \
189 ** * Any of the following characters, unquoted: ;|& or \n except
190 ** these characters are allowed as the very last character in the
191 ** string.
192 ** * Unbalanced single or double quotes
193 **
194 ** This routine is intended as a second line of defense against attack.
195 ** It should never fail. Dangerous shell strings should be detected and
196 ** fixed before calling fossil_system(). This routine serves only as a
197 ** safety net in case of bugs elsewhere in the system.
198 **
199 ** If an unsafe string is seen, either abort (default) or print
200 ** a warning message (if safeCmdStrTest is true).
201 */
fossil_assert_safe_command_string(const char * z)202 static void fossil_assert_safe_command_string(const char *z){
203 int unsafe = 0;
204 #ifndef _WIN32
205 /* Unix */
206 int inQuote = 0;
207 int i, c;
208 for(i=0; !unsafe && (c = z[i])!=0; i++){
209 switch( c ){
210 case '$':
211 case '`': {
212 if( inQuote!='\'' ) unsafe = i+1;
213 break;
214 }
215 case ';':
216 case '|':
217 case '&':
218 case '\n': {
219 if( inQuote!='\'' && z[i+1]!=0 ) unsafe = i+1;
220 break;
221 }
222 case '"':
223 case '\'': {
224 if( inQuote==0 ){
225 inQuote = c;
226 }else if( inQuote==c ){
227 inQuote = 0;
228 }
229 break;
230 }
231 case '\\': {
232 if( z[i+1]==0 ){
233 unsafe = i+1;
234 }else if( inQuote!='\'' ){
235 i++;
236 }
237 break;
238 }
239 }
240 }
241 if( inQuote ) unsafe = i;
242 #else
243 /* Windows */
244 int i, c;
245 int inQuote = 0;
246 for(i=0; !unsafe && (c = z[i])!=0; i++){
247 switch( c ){
248 case '>':
249 case '<':
250 case '|':
251 case '&':
252 case '\n': {
253 if( inQuote==0 && z[i+1]!=0 ) unsafe = i+1;
254 break;
255 }
256 case '"': {
257 if( inQuote==c ){
258 inQuote = 0;
259 }else{
260 inQuote = c;
261 }
262 break;
263 }
264 case '^': {
265 if( !inQuote && z[i+1]!=0 ){
266 i++;
267 }
268 break;
269 }
270 }
271 }
272 if( inQuote ) unsafe = i;
273 #endif
274 if( unsafe && safeCmdStrTest<2 ){
275 char *zMsg = mprintf("Unsafe command string: %s\n%*shere ----^",
276 z, unsafe+13, "");
277 if( safeCmdStrTest ){
278 fossil_print("%z\n", zMsg);
279 fossil_free(zMsg);
280 }else{
281 fossil_panic("%s", zMsg);
282 }
283 }
284 }
285
286 /*
287 ** This function implements a cross-platform "system()" interface.
288 */
fossil_system(const char * zOrigCmd)289 int fossil_system(const char *zOrigCmd){
290 int rc;
291 #if defined(_WIN32)
292 /* On windows, we have to put double-quotes around the entire command.
293 ** Who knows why - this is just the way windows works.
294 */
295 char *zNewCmd = mprintf("\"%s\"", zOrigCmd);
296 wchar_t *zUnicode = fossil_utf8_to_unicode(zNewCmd);
297 if( g.fSystemTrace ) {
298 fossil_trace("SYSTEM: %s\n", zNewCmd);
299 }
300 fossil_assert_safe_command_string(zOrigCmd);
301 rc = _wsystem(zUnicode);
302 fossil_unicode_free(zUnicode);
303 free(zNewCmd);
304 #else
305 /* On unix, evaluate the command directly.
306 */
307 if( g.fSystemTrace ) fprintf(stderr, "SYSTEM: %s\n", zOrigCmd);
308 fossil_assert_safe_command_string(zOrigCmd);
309
310 /* Unix systems should never shell-out while processing an HTTP request,
311 ** either via CGI, SCGI, or direct HTTP. The following assert verifies
312 ** this. And the following assert proves that Fossil is not vulnerable
313 ** to the ShellShock or BashDoor bug.
314 */
315 assert( g.cgiOutput==0 );
316
317 /* The regular system() call works to get a shell on unix */
318 fossil_limit_memory(0);
319 rc = system(zOrigCmd);
320 fossil_limit_memory(1);
321 #endif
322 return rc;
323 }
324
325 /*
326 ** Like "fossil_system()" but does not check the command-string for
327 ** potential security problems.
328 */
fossil_unsafe_system(const char * zOrigCmd)329 int fossil_unsafe_system(const char *zOrigCmd){
330 int rc;
331 safeCmdStrTest = 2;
332 rc = fossil_system(zOrigCmd);
333 safeCmdStrTest = 0;
334 return rc;
335 }
336
337 /*
338 ** COMMAND: test-fossil-system
339 **
340 ** Read lines of input and send them to fossil_system() for evaluation.
341 ** Use this command to verify that fossil_system() will not run "unsafe"
342 ** commands.
343 */
test_fossil_system_cmd(void)344 void test_fossil_system_cmd(void){
345 char zLine[10000];
346 safeCmdStrTest = 1;
347 while(1){
348 size_t n;
349 int rc;
350 printf("system-test> ");
351 fflush(stdout);
352 if( !fgets(zLine, sizeof(zLine), stdin) ) break;
353 n = strlen(zLine);
354 while( n>0 && fossil_isspace(zLine[n-1]) ) n--;
355 zLine[n] = 0;
356 printf("cmd: [%s]\n", zLine);
357 fflush(stdout);
358 rc = fossil_system(zLine);
359 printf("result: %d\n", rc);
360 }
361 }
362
363 /*
364 ** Like strcmp() except that it accepts NULL pointers. NULL sorts before
365 ** all non-NULL string pointers. Also, this strcmp() is a binary comparison
366 ** that does not consider locale.
367 */
fossil_strcmp(const char * zA,const char * zB)368 int fossil_strcmp(const char *zA, const char *zB){
369 if( zA==0 ){
370 if( zB==0 ) return 0;
371 return -1;
372 }else if( zB==0 ){
373 return +1;
374 }else{
375 return strcmp(zA,zB);
376 }
377 }
fossil_strncmp(const char * zA,const char * zB,int nByte)378 int fossil_strncmp(const char *zA, const char *zB, int nByte){
379 if( zA==0 ){
380 if( zB==0 ) return 0;
381 return -1;
382 }else if( zB==0 ){
383 return +1;
384 }else if( nByte>0 ){
385 int a, b;
386 do{
387 a = *zA++;
388 b = *zB++;
389 }while( a==b && a!=0 && (--nByte)>0 );
390 return ((unsigned char)a) - (unsigned char)b;
391 }else{
392 return 0;
393 }
394 }
395
396 /*
397 ** Case insensitive string comparison.
398 */
fossil_strnicmp(const char * zA,const char * zB,int nByte)399 int fossil_strnicmp(const char *zA, const char *zB, int nByte){
400 if( zA==0 ){
401 if( zB==0 ) return 0;
402 return -1;
403 }else if( zB==0 ){
404 return +1;
405 }
406 if( nByte<0 ) nByte = strlen(zB);
407 return sqlite3_strnicmp(zA, zB, nByte);
408 }
fossil_stricmp(const char * zA,const char * zB)409 int fossil_stricmp(const char *zA, const char *zB){
410 int nByte;
411 int rc;
412 if( zA==0 ){
413 if( zB==0 ) return 0;
414 return -1;
415 }else if( zB==0 ){
416 return +1;
417 }
418 nByte = strlen(zB);
419 rc = sqlite3_strnicmp(zA, zB, nByte);
420 if( rc==0 && zA[nByte] ) rc = 1;
421 return rc;
422 }
423
424 /*
425 ** Get user and kernel times in microseconds.
426 */
fossil_cpu_times(sqlite3_uint64 * piUser,sqlite3_uint64 * piKernel)427 void fossil_cpu_times(sqlite3_uint64 *piUser, sqlite3_uint64 *piKernel){
428 #ifdef _WIN32
429 FILETIME not_used;
430 FILETIME kernel_time;
431 FILETIME user_time;
432 GetProcessTimes(GetCurrentProcess(), ¬_used, ¬_used,
433 &kernel_time, &user_time);
434 if( piUser ){
435 *piUser = ((((sqlite3_uint64)user_time.dwHighDateTime)<<32) +
436 (sqlite3_uint64)user_time.dwLowDateTime + 5)/10;
437 }
438 if( piKernel ){
439 *piKernel = ((((sqlite3_uint64)kernel_time.dwHighDateTime)<<32) +
440 (sqlite3_uint64)kernel_time.dwLowDateTime + 5)/10;
441 }
442 #else
443 struct rusage s;
444 getrusage(RUSAGE_SELF, &s);
445 if( piUser ){
446 *piUser = ((sqlite3_uint64)s.ru_utime.tv_sec)*1000000 + s.ru_utime.tv_usec;
447 }
448 if( piKernel ){
449 *piKernel =
450 ((sqlite3_uint64)s.ru_stime.tv_sec)*1000000 + s.ru_stime.tv_usec;
451 }
452 #endif
453 }
454
455 /*
456 ** Return the resident set size for this process
457 */
fossil_rss(void)458 sqlite3_uint64 fossil_rss(void){
459 #ifdef _WIN32
460 return 0;
461 #else
462 struct rusage s;
463 getrusage(RUSAGE_SELF, &s);
464 return s.ru_maxrss*1024;
465 #endif
466 }
467
468
469 /*
470 ** Internal helper type for fossil_timer_xxx().
471 */
472 enum FossilTimerEnum {
473 FOSSIL_TIMER_COUNT = 10 /* Number of timers we can track. */
474 };
475 static struct FossilTimer {
476 sqlite3_uint64 u; /* "User" CPU times */
477 sqlite3_uint64 s; /* "System" CPU times */
478 int id; /* positive if allocated, else 0. */
479 } fossilTimerList[FOSSIL_TIMER_COUNT] = {{0,0,0}};
480
481 /*
482 ** Stores the current CPU times into the shared timer list
483 ** and returns that timer's internal ID. Pass that ID to
484 ** fossil_timer_fetch() to get the elapsed time for that
485 ** timer.
486 **
487 ** The system has a fixed number of timers, and they can be
488 ** "deallocated" by passing this function's return value to
489 ** fossil_timer_stop() Adjust FOSSIL_TIMER_COUNT to set the number of
490 ** available timers.
491 **
492 ** Returns 0 on error (no more timers available), with 1+ being valid
493 ** timer IDs.
494 */
fossil_timer_start()495 int fossil_timer_start(){
496 int i;
497 for( i = 0; i < FOSSIL_TIMER_COUNT; ++i ){
498 struct FossilTimer * ft = &fossilTimerList[i];
499 if(ft->id) continue;
500 ft->id = i+1;
501 fossil_cpu_times( &ft->u, &ft->s );
502 break;
503 }
504 return (i<FOSSIL_TIMER_COUNT) ? i+1 : 0;
505 }
506
507 /*
508 ** Returns the difference in CPU times in microseconds since
509 ** fossil_timer_start() was called and returned the given timer ID (or
510 ** since it was last reset). Returns 0 if timerId is out of range.
511 */
fossil_timer_fetch(int timerId)512 sqlite3_uint64 fossil_timer_fetch(int timerId){
513 if( timerId>0 && timerId<=FOSSIL_TIMER_COUNT ){
514 struct FossilTimer * start = &fossilTimerList[timerId-1];
515 if( !start->id ){
516 fossil_panic("Invalid call to fetch a non-allocated "
517 "timer (#%d)", timerId);
518 /*NOTREACHED*/
519 }else{
520 sqlite3_uint64 eu = 0, es = 0;
521 fossil_cpu_times( &eu, &es );
522 return (eu - start->u) + (es - start->s);
523 }
524 }
525 return 0;
526 }
527
528 /*
529 ** Resets the timer associated with the given ID, as obtained via
530 ** fossil_timer_start(), to the current CPU time values.
531 */
fossil_timer_reset(int timerId)532 sqlite3_uint64 fossil_timer_reset(int timerId){
533 if( timerId>0 && timerId<=FOSSIL_TIMER_COUNT ){
534 struct FossilTimer * start = &fossilTimerList[timerId-1];
535 if( !start->id ){
536 fossil_panic("Invalid call to reset a non-allocated "
537 "timer (#%d)", timerId);
538 /*NOTREACHED*/
539 }else{
540 sqlite3_uint64 const rc = fossil_timer_fetch(timerId);
541 fossil_cpu_times( &start->u, &start->s );
542 return rc;
543 }
544 }
545 return 0;
546 }
547
548 /**
549 "Deallocates" the fossil timer identified by the given timer ID.
550 returns the difference (in uSec) between the last time that timer
551 was started or reset. Returns 0 if timerId is out of range (but
552 note that, due to system-level precision restrictions, this
553 function might return 0 on success, too!). It is not legal to
554 re-use the passed-in timerId after calling this until/unless it is
555 re-initialized using fossil_timer_start() (NOT
556 fossil_timer_reset()).
557 */
fossil_timer_stop(int timerId)558 sqlite3_uint64 fossil_timer_stop(int timerId){
559 if(timerId<1 || timerId>FOSSIL_TIMER_COUNT){
560 return 0;
561 }else{
562 sqlite3_uint64 const rc = fossil_timer_fetch(timerId);
563 struct FossilTimer * t = &fossilTimerList[timerId-1];
564 t->id = 0;
565 t->u = t->s = 0U;
566 return rc;
567 }
568 }
569
570 /*
571 ** Returns true (non-0) if the given timer ID (as returned from
572 ** fossil_timer_start() is currently active.
573 */
fossil_timer_is_active(int timerId)574 int fossil_timer_is_active( int timerId ){
575 if(timerId<1 || timerId>FOSSIL_TIMER_COUNT){
576 return 0;
577 }else{
578 const int rc = fossilTimerList[timerId-1].id;
579 assert(!rc || (rc == timerId));
580 return fossilTimerList[timerId-1].id;
581 }
582 }
583
584 /*
585 ** Return TRUE if fd is a valid open file descriptor. This only
586 ** works on unix. The function always returns true on Windows.
587 */
is_valid_fd(int fd)588 int is_valid_fd(int fd){
589 #ifdef _WIN32
590 return 1;
591 #else
592 return fcntl(fd, F_GETFL)!=(-1) || errno!=EBADF;
593 #endif
594 }
595
596 /*
597 ** Returns TRUE if zSym is exactly HNAME_LEN_SHA1 or HNAME_LEN_K256
598 ** bytes long and contains only lower-case ASCII hexadecimal values.
599 */
fossil_is_artifact_hash(const char * zSym)600 int fossil_is_artifact_hash(const char *zSym){
601 int sz = zSym ? (int)strlen(zSym) : 0;
602 return (HNAME_LEN_SHA1==sz || HNAME_LEN_K256==sz) && validate16(zSym, sz);
603 }
604
605 /*
606 ** Return true if the input string is NULL or all whitespace.
607 ** Return false if the input string contains text.
608 */
fossil_all_whitespace(const char * z)609 int fossil_all_whitespace(const char *z){
610 if( z==0 ) return 1;
611 while( fossil_isspace(z[0]) ){ z++; }
612 return z[0]==0;
613 }
614
615 /*
616 ** Return the name of the users preferred text editor. Return NULL if
617 ** not found.
618 **
619 ** Search algorithm:
620 ** (1) The local "editor" setting
621 ** (2) The global "editor" setting
622 ** (3) The VISUAL environment variable
623 ** (4) The EDITOR environment variable
624 ** (5) (Windows only:) "notepad.exe"
625 */
fossil_text_editor(void)626 const char *fossil_text_editor(void){
627 const char *zEditor = db_get("editor", 0);
628 if( zEditor==0 ){
629 zEditor = fossil_getenv("VISUAL");
630 }
631 if( zEditor==0 ){
632 zEditor = fossil_getenv("EDITOR");
633 }
634 #if defined(_WIN32) || defined(__CYGWIN__)
635 if( zEditor==0 ){
636 zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SYSTEMROOT"));
637 #if defined(__CYGWIN__)
638 zEditor = fossil_utf8_to_path(zEditor, 0);
639 #endif
640 }
641 #endif
642 return zEditor;
643 }
644
645 /*
646 ** Construct a temporary filename.
647 **
648 ** The returned string is obtained from sqlite3_malloc() and must be
649 ** freed by the caller.
650 */
fossil_temp_filename(void)651 char *fossil_temp_filename(void){
652 char *zTFile = 0;
653 const char *zDir;
654 char cDirSep;
655 char zSep[2];
656 size_t nDir;
657 u64 r[2];
658 #ifdef _WIN32
659 char *zTempDirA = NULL;
660 WCHAR zTempDirW[MAX_PATH+1];
661 const DWORD dwTempSizeW = sizeof(zTempDirW)/sizeof(zTempDirW[0]);
662 DWORD dwTempLenW;
663 #else
664 int i;
665 static const char *azTmp[] = {"/var/tmp","/usr/tmp","/tmp"};
666 #endif
667 if( g.db ){
668 sqlite3_file_control(g.db, 0, SQLITE_FCNTL_TEMPFILENAME, (void*)&zTFile);
669 if( zTFile ) return zTFile;
670 }
671 sqlite3_randomness(sizeof(r), &r);
672 #if _WIN32
673 cDirSep = '\\';
674 dwTempLenW = GetTempPathW(dwTempSizeW, zTempDirW);
675 if( dwTempLenW>0 && dwTempLenW<dwTempSizeW
676 && ( zTempDirA = fossil_path_to_utf8(zTempDirW) )){
677 zDir = zTempDirA;
678 }else{
679 zDir = fossil_getenv("LOCALAPPDATA");
680 if( zDir==0 ) zDir = ".";
681 }
682 #else
683 for(i=0; i<sizeof(azTmp)/sizeof(azTmp[0]); i++){
684 struct stat buf;
685 zDir = azTmp[i];
686 if( stat(zDir,&buf)==0 && S_ISDIR(buf.st_mode) && access(zDir,03)==0 ){
687 break;
688 }
689 }
690 if( i>=sizeof(azTmp)/sizeof(azTmp[0]) ) zDir = ".";
691 cDirSep = '/';
692 #endif
693 nDir = strlen(zDir);
694 zSep[1] = 0;
695 zSep[0] = (nDir && zDir[nDir-1]==cDirSep) ? 0 : cDirSep;
696 zTFile = sqlite3_mprintf("%s%sfossil%016llx%016llx", zDir,zSep,r[0],r[1]);
697 #ifdef _WIN32
698 if( zTempDirA ) fossil_path_free(zTempDirA);
699 #endif
700 return zTFile;
701 }
702
703 /*
704 ** Turn memory limits for stack and heap on and off. The argument
705 ** is true to turn memory limits on and false to turn them off.
706 **
707 ** Memory limits should be enabled at startup, but then turned off
708 ** before starting subprocesses.
709 */
fossil_limit_memory(int onOff)710 void fossil_limit_memory(int onOff){
711 #if defined(__unix__)
712 static sqlite3_int64 origHeap = 10000000000LL; /* 10GB */
713 static sqlite3_int64 origStack = 8000000 ; /* 8MB */
714 struct rlimit x;
715
716 #if defined(RLIMIT_DATA)
717 getrlimit(RLIMIT_DATA, &x);
718 if( onOff ){
719 origHeap = x.rlim_cur;
720 if( sizeof(void*)<8 || sizeof(x.rlim_cur)<8 ){
721 x.rlim_cur = 1000000000 ; /* 1GB on 32-bit systems */
722 }else{
723 x.rlim_cur = 10000000000LL; /* 10GB on 64-bit systems */
724 }
725 }else{
726 x.rlim_cur = origHeap;
727 }
728 setrlimit(RLIMIT_DATA, &x);
729 #endif /* defined(RLIMIT_DATA) */
730 #if defined(RLIMIT_STACK)
731 getrlimit(RLIMIT_STACK, &x);
732 if( onOff ){
733 origStack = x.rlim_cur;
734 x.rlim_cur = 8000000; /* 8MB */
735 }else{
736 x.rlim_cur = origStack;
737 }
738 setrlimit(RLIMIT_STACK, &x);
739 #endif /* defined(RLIMIT_STACK) */
740 #endif /* defined(__unix__) */
741 }
742
743 #if defined(HAVE_PLEDGE)
744 /*
745 ** Interface to pledge() on OpenBSD 5.9 and later.
746 **
747 ** On platforms that have pledge(), use this routine.
748 ** On all other platforms, this routine does not exist, but instead
749 ** a macro defined in config.h is used to provide a no-op.
750 */
fossil_pledge(const char * promises)751 void fossil_pledge(const char *promises){
752 if( pledge(promises, 0) ){
753 fossil_panic("pledge(\"%s\",NULL) fails with errno=%d",
754 promises, (int)errno);
755 }
756 }
757 #endif /* defined(HAVE_PLEDGE) */
758
759 /*
760 ** Construct a random password and return it as a string. N is the
761 ** recommended number of characters for the password.
762 **
763 ** Space to hold the returned string is obtained from fossil_malloc()
764 ** and should be freed by the caller.
765 */
fossil_random_password(int N)766 char *fossil_random_password(int N){
767 char zSrc[60];
768 int nSrc;
769 int i;
770 char z[60];
771
772 /* Source characters for the password. Omit characters like "0", "O",
773 ** "1" and "I" that might be easily confused */
774 static const char zAlphabet[] =
775 /* 0 1 2 3 4 5 */
776 /* 123456789 123456789 123456789 123456789 123456789 123456 */
777 "23456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
778
779 if( N<8 ) N = 8;
780 nSrc = sizeof(zAlphabet) - 1;
781 if( N>nSrc ) N = nSrc;
782 memcpy(zSrc, zAlphabet, nSrc);
783
784 for(i=0; i<N; i++){
785 unsigned r;
786 sqlite3_randomness(sizeof(r), &r);
787 r %= nSrc;
788 z[i] = zSrc[r];
789 zSrc[r] = zSrc[--nSrc];
790 }
791 z[i] = 0;
792 return fossil_strdup(z);
793 }
794
795 /*
796 ** COMMAND: test-random-password
797 **
798 ** Usage: %fossil test-random-password [N] [--entropy]
799 **
800 ** Generate a random password string of approximately N characters in length.
801 ** If N is omitted, use 12. Values of N less than 8 are changed to 8
802 ** and greater than 57 and changed to 57.
803 **
804 ** If the --entropy flag is included, the number of bits of entropy in
805 ** the password is show as well.
806 */
test_random_password(void)807 void test_random_password(void){
808 int N = 12;
809 int showEntropy = 0;
810 int i;
811 char *zPassword;
812 for(i=2; i<g.argc; i++){
813 const char *z = g.argv[i];
814 if( z[0]=='-' && z[1]=='-' ) z++;
815 if( strcmp(z,"-entropy")==0 ){
816 showEntropy = 1;
817 }else if( fossil_isdigit(z[0]) ){
818 N = atoi(z);
819 if( N<8 ) N = 8;
820 if( N>57 ) N = 57;
821 }else{
822 usage("[N] [--entropy]");
823 }
824 }
825 zPassword = fossil_random_password(N);
826 if( showEntropy ){
827 double et = 57.0;
828 for(i=1; i<N; i++) et *= 57-i;
829 fossil_print("%s (%d bits of entropy)\n", zPassword,
830 (int)(log(et)/log(2.0)));
831 }else{
832 fossil_print("%s\n", zPassword);
833 }
834 fossil_free(zPassword);
835 }
836
837 /*
838 ** Return the number of decimal digits in a nonnegative integer. This is useful
839 ** when formatting text.
840 */
fossil_num_digits(int n)841 int fossil_num_digits(int n){
842 return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3
843 : n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6
844 : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10;
845 }
846
847 #if !defined(_WIN32)
848 #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
849 /*
850 ** Search for an executable on the PATH environment variable.
851 ** Return true (1) if found and false (0) if not found.
852 */
binaryOnPath(const char * zBinary)853 static int binaryOnPath(const char *zBinary){
854 const char *zPath = fossil_getenv("PATH");
855 char *zFull;
856 int i;
857 int bExists;
858 while( zPath && zPath[0] ){
859 while( zPath[0]==':' ) zPath++;
860 for(i=0; zPath[i] && zPath[i]!=':'; i++){}
861 zFull = mprintf("%.*s/%s", i, zPath, zBinary);
862 bExists = file_access(zFull, X_OK);
863 fossil_free(zFull);
864 if( bExists==0 ) return 1;
865 zPath += i;
866 }
867 return 0;
868 }
869 #endif
870 #endif
871
872
873 /*
874 ** Return the name of a command that will launch a web-browser.
875 */
fossil_web_browser(void)876 const char *fossil_web_browser(void){
877 const char *zBrowser = 0;
878 #if defined(_WIN32)
879 zBrowser = db_get("web-browser", "start \"\"");
880 #elif defined(__DARWIN__) || defined(__APPLE__) || defined(__HAIKU__)
881 zBrowser = db_get("web-browser", "open");
882 #else
883 zBrowser = db_get("web-browser", 0);
884 if( zBrowser==0 ){
885 static const char *const azBrowserProg[] =
886 { "xdg-open", "gnome-open", "firefox", "google-chrome" };
887 int i;
888 zBrowser = "echo";
889 for(i=0; i<count(azBrowserProg); i++){
890 if( binaryOnPath(azBrowserProg[i]) ){
891 zBrowser = azBrowserProg[i];
892 break;
893 }
894 }
895 }
896 #endif
897 return zBrowser;
898 }
899