Fossil SCM
Use the Windows _wspawnv() interface with the _P_NOWAIT option to start a separate backoffice process whenever necessary. Add the backoffice-logfile setting for monitoring backoffice operation.
Commit
2583cae18a4bcf03408e3f56544a53de23ea53876a1551fa7948ef5ee0dcb289
Parent
b3ccc4bf34a525a…
2 files changed
+41
-2
+7
+41
-2
| --- src/backoffice.c | ||
| +++ src/backoffice.c | ||
| @@ -60,10 +60,12 @@ | ||
| 60 | 60 | #include "config.h" |
| 61 | 61 | #include "backoffice.h" |
| 62 | 62 | #include <time.h> |
| 63 | 63 | #if defined(_WIN32) |
| 64 | 64 | # include <windows.h> |
| 65 | +# include <stdio.h> | |
| 66 | +# include <process.h> | |
| 65 | 67 | #else |
| 66 | 68 | # include <unistd.h> |
| 67 | 69 | # include <sys/types.h> |
| 68 | 70 | # include <signal.h> |
| 69 | 71 | #endif |
| @@ -374,10 +376,24 @@ | ||
| 374 | 376 | /* |
| 375 | 377 | ** This routine runs to do the backoffice processing. When adding new |
| 376 | 378 | ** backoffice processing tasks, add them here. |
| 377 | 379 | */ |
| 378 | 380 | void backoffice_work(void){ |
| 381 | + /* Log the backoffice run for testing purposes. For production deployments | |
| 382 | + ** the "backoffice-logfile" property should be unset and the following code | |
| 383 | + ** should be a no-op. */ | |
| 384 | + char *zLog = db_get("backoffice-logfile",0); | |
| 385 | + if( zLog && zLog[0] ){ | |
| 386 | + FILE *pLog = fossil_fopen(zLog, "a"); | |
| 387 | + if( pLog ){ | |
| 388 | + char *zDate = db_text(0, "SELECT datetime('now');"); | |
| 389 | + fprintf(pLog, "%s (%d) backoffice running\n", zDate, getpid()); | |
| 390 | + fclose(pLog); | |
| 391 | + } | |
| 392 | + } | |
| 393 | + | |
| 394 | + /* Here is where the actual work of the backoffice happens */ | |
| 379 | 395 | email_backoffice(0); |
| 380 | 396 | } |
| 381 | 397 | |
| 382 | 398 | /* |
| 383 | 399 | ** COMMAND: backoffice |
| @@ -386,12 +402,13 @@ | ||
| 386 | 402 | ** |
| 387 | 403 | ** Run backoffice processing. This might be done by a cron job or |
| 388 | 404 | ** similar to make sure backoffice processing happens periodically. |
| 389 | 405 | */ |
| 390 | 406 | void backoffice_command(void){ |
| 391 | - verify_all_options(); | |
| 407 | + if( find_option("trace",0,0)!=0 ) g.fAnyTrace = 1; | |
| 392 | 408 | db_find_and_open_repository(0,0); |
| 409 | + verify_all_options(); | |
| 393 | 410 | backoffice_thread(); |
| 394 | 411 | } |
| 395 | 412 | |
| 396 | 413 | /* |
| 397 | 414 | ** This is the main interface to backoffice from the rest of the system. |
| @@ -401,11 +418,32 @@ | ||
| 401 | 418 | void backoffice_run_if_needed(void){ |
| 402 | 419 | if( backofficeDb==0 ) return; |
| 403 | 420 | if( strcmp(backofficeDb,"x")==0 ) return; |
| 404 | 421 | if( g.db ) return; |
| 405 | 422 | if( g.repositoryOpen ) return; |
| 406 | -#if !defined(_WIN32) | |
| 423 | +#if defined(_WIN32) | |
| 424 | + { | |
| 425 | + int i; | |
| 426 | + intptr_t x; | |
| 427 | + char *argv[4]; | |
| 428 | + wchar_t *ax[5]; | |
| 429 | + argv[0] = g.nameOfExe; | |
| 430 | + argv[1] = "backoffice"; | |
| 431 | + argv[2] = "-R"; | |
| 432 | + argv[3] = backofficeDb; | |
| 433 | + ax[4] = 0; | |
| 434 | + for(i=0; i<=3; i++) ax[i] = fossil_utf8_to_unicode(argv[i]); | |
| 435 | + x = _wspawnv(_P_NOWAIT, ax[0], ax); | |
| 436 | + for(i=0; i<=3; i++) fossil_unicode_free(ax[i]); | |
| 437 | + if( g.fAnyTrace ){ | |
| 438 | + fprintf(stderr, | |
| 439 | + "/***** Subprocess %d creates backoffice child %d *****/\n", | |
| 440 | + getpid(), (int)x); | |
| 441 | + } | |
| 442 | + if( x>=0 ) return; | |
| 443 | + } | |
| 444 | +#else /* unix */ | |
| 407 | 445 | { |
| 408 | 446 | pid_t pid = fork(); |
| 409 | 447 | if( pid>0 ){ |
| 410 | 448 | /* This is the parent in a successful fork(). Return immediately. */ |
| 411 | 449 | if( g.fAnyTrace ){ |
| @@ -415,10 +453,11 @@ | ||
| 415 | 453 | } |
| 416 | 454 | return; |
| 417 | 455 | } |
| 418 | 456 | if( pid==0 ){ |
| 419 | 457 | /* This is the child of a successful fork(). Run backoffice. */ |
| 458 | + setsid(); | |
| 420 | 459 | db_open_repository(backofficeDb); |
| 421 | 460 | backofficeDb = "x"; |
| 422 | 461 | backoffice_thread(); |
| 423 | 462 | db_close(1); |
| 424 | 463 | if( g.fAnyTrace ){ |
| 425 | 464 |
| --- src/backoffice.c | |
| +++ src/backoffice.c | |
| @@ -60,10 +60,12 @@ | |
| 60 | #include "config.h" |
| 61 | #include "backoffice.h" |
| 62 | #include <time.h> |
| 63 | #if defined(_WIN32) |
| 64 | # include <windows.h> |
| 65 | #else |
| 66 | # include <unistd.h> |
| 67 | # include <sys/types.h> |
| 68 | # include <signal.h> |
| 69 | #endif |
| @@ -374,10 +376,24 @@ | |
| 374 | /* |
| 375 | ** This routine runs to do the backoffice processing. When adding new |
| 376 | ** backoffice processing tasks, add them here. |
| 377 | */ |
| 378 | void backoffice_work(void){ |
| 379 | email_backoffice(0); |
| 380 | } |
| 381 | |
| 382 | /* |
| 383 | ** COMMAND: backoffice |
| @@ -386,12 +402,13 @@ | |
| 386 | ** |
| 387 | ** Run backoffice processing. This might be done by a cron job or |
| 388 | ** similar to make sure backoffice processing happens periodically. |
| 389 | */ |
| 390 | void backoffice_command(void){ |
| 391 | verify_all_options(); |
| 392 | db_find_and_open_repository(0,0); |
| 393 | backoffice_thread(); |
| 394 | } |
| 395 | |
| 396 | /* |
| 397 | ** This is the main interface to backoffice from the rest of the system. |
| @@ -401,11 +418,32 @@ | |
| 401 | void backoffice_run_if_needed(void){ |
| 402 | if( backofficeDb==0 ) return; |
| 403 | if( strcmp(backofficeDb,"x")==0 ) return; |
| 404 | if( g.db ) return; |
| 405 | if( g.repositoryOpen ) return; |
| 406 | #if !defined(_WIN32) |
| 407 | { |
| 408 | pid_t pid = fork(); |
| 409 | if( pid>0 ){ |
| 410 | /* This is the parent in a successful fork(). Return immediately. */ |
| 411 | if( g.fAnyTrace ){ |
| @@ -415,10 +453,11 @@ | |
| 415 | } |
| 416 | return; |
| 417 | } |
| 418 | if( pid==0 ){ |
| 419 | /* This is the child of a successful fork(). Run backoffice. */ |
| 420 | db_open_repository(backofficeDb); |
| 421 | backofficeDb = "x"; |
| 422 | backoffice_thread(); |
| 423 | db_close(1); |
| 424 | if( g.fAnyTrace ){ |
| 425 |
| --- src/backoffice.c | |
| +++ src/backoffice.c | |
| @@ -60,10 +60,12 @@ | |
| 60 | #include "config.h" |
| 61 | #include "backoffice.h" |
| 62 | #include <time.h> |
| 63 | #if defined(_WIN32) |
| 64 | # include <windows.h> |
| 65 | # include <stdio.h> |
| 66 | # include <process.h> |
| 67 | #else |
| 68 | # include <unistd.h> |
| 69 | # include <sys/types.h> |
| 70 | # include <signal.h> |
| 71 | #endif |
| @@ -374,10 +376,24 @@ | |
| 376 | /* |
| 377 | ** This routine runs to do the backoffice processing. When adding new |
| 378 | ** backoffice processing tasks, add them here. |
| 379 | */ |
| 380 | void backoffice_work(void){ |
| 381 | /* Log the backoffice run for testing purposes. For production deployments |
| 382 | ** the "backoffice-logfile" property should be unset and the following code |
| 383 | ** should be a no-op. */ |
| 384 | char *zLog = db_get("backoffice-logfile",0); |
| 385 | if( zLog && zLog[0] ){ |
| 386 | FILE *pLog = fossil_fopen(zLog, "a"); |
| 387 | if( pLog ){ |
| 388 | char *zDate = db_text(0, "SELECT datetime('now');"); |
| 389 | fprintf(pLog, "%s (%d) backoffice running\n", zDate, getpid()); |
| 390 | fclose(pLog); |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | /* Here is where the actual work of the backoffice happens */ |
| 395 | email_backoffice(0); |
| 396 | } |
| 397 | |
| 398 | /* |
| 399 | ** COMMAND: backoffice |
| @@ -386,12 +402,13 @@ | |
| 402 | ** |
| 403 | ** Run backoffice processing. This might be done by a cron job or |
| 404 | ** similar to make sure backoffice processing happens periodically. |
| 405 | */ |
| 406 | void backoffice_command(void){ |
| 407 | if( find_option("trace",0,0)!=0 ) g.fAnyTrace = 1; |
| 408 | db_find_and_open_repository(0,0); |
| 409 | verify_all_options(); |
| 410 | backoffice_thread(); |
| 411 | } |
| 412 | |
| 413 | /* |
| 414 | ** This is the main interface to backoffice from the rest of the system. |
| @@ -401,11 +418,32 @@ | |
| 418 | void backoffice_run_if_needed(void){ |
| 419 | if( backofficeDb==0 ) return; |
| 420 | if( strcmp(backofficeDb,"x")==0 ) return; |
| 421 | if( g.db ) return; |
| 422 | if( g.repositoryOpen ) return; |
| 423 | #if defined(_WIN32) |
| 424 | { |
| 425 | int i; |
| 426 | intptr_t x; |
| 427 | char *argv[4]; |
| 428 | wchar_t *ax[5]; |
| 429 | argv[0] = g.nameOfExe; |
| 430 | argv[1] = "backoffice"; |
| 431 | argv[2] = "-R"; |
| 432 | argv[3] = backofficeDb; |
| 433 | ax[4] = 0; |
| 434 | for(i=0; i<=3; i++) ax[i] = fossil_utf8_to_unicode(argv[i]); |
| 435 | x = _wspawnv(_P_NOWAIT, ax[0], ax); |
| 436 | for(i=0; i<=3; i++) fossil_unicode_free(ax[i]); |
| 437 | if( g.fAnyTrace ){ |
| 438 | fprintf(stderr, |
| 439 | "/***** Subprocess %d creates backoffice child %d *****/\n", |
| 440 | getpid(), (int)x); |
| 441 | } |
| 442 | if( x>=0 ) return; |
| 443 | } |
| 444 | #else /* unix */ |
| 445 | { |
| 446 | pid_t pid = fork(); |
| 447 | if( pid>0 ){ |
| 448 | /* This is the parent in a successful fork(). Return immediately. */ |
| 449 | if( g.fAnyTrace ){ |
| @@ -415,10 +453,11 @@ | |
| 453 | } |
| 454 | return; |
| 455 | } |
| 456 | if( pid==0 ){ |
| 457 | /* This is the child of a successful fork(). Run backoffice. */ |
| 458 | setsid(); |
| 459 | db_open_repository(backofficeDb); |
| 460 | backofficeDb = "x"; |
| 461 | backoffice_thread(); |
| 462 | db_close(1); |
| 463 | if( g.fAnyTrace ){ |
| 464 |
M
src/db.c
+7
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -3033,10 +3033,17 @@ | ||
| 3033 | 3033 | ** SETTING: backoffice-nodelay boolean default=on |
| 3034 | 3034 | ** If backoffice-nodelay is true, then the backoffice processing |
| 3035 | 3035 | ** will never invoke sleep(). If it has nothing useful to do, |
| 3036 | 3036 | ** it simply exits. |
| 3037 | 3037 | */ |
| 3038 | +/* | |
| 3039 | +** SETTING: backoffice-logfile width=40 | |
| 3040 | +** If backoffice-logfile is not an empty string and is a valid | |
| 3041 | +** filename, then a one-line message is appended to that file | |
| 3042 | +** every time the backoffice runs. This can be used for debugging, | |
| 3043 | +** to ensure that backoffice is running appropriately. | |
| 3044 | +*/ | |
| 3038 | 3045 | /* |
| 3039 | 3046 | ** SETTING: binary-glob width=40 versionable block-text |
| 3040 | 3047 | ** The VALUE of this setting is a comma or newline-separated list of |
| 3041 | 3048 | ** GLOB patterns that should be treated as binary files |
| 3042 | 3049 | ** for committing and merging purposes. Example: *.jpg |
| 3043 | 3050 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -3033,10 +3033,17 @@ | |
| 3033 | ** SETTING: backoffice-nodelay boolean default=on |
| 3034 | ** If backoffice-nodelay is true, then the backoffice processing |
| 3035 | ** will never invoke sleep(). If it has nothing useful to do, |
| 3036 | ** it simply exits. |
| 3037 | */ |
| 3038 | /* |
| 3039 | ** SETTING: binary-glob width=40 versionable block-text |
| 3040 | ** The VALUE of this setting is a comma or newline-separated list of |
| 3041 | ** GLOB patterns that should be treated as binary files |
| 3042 | ** for committing and merging purposes. Example: *.jpg |
| 3043 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -3033,10 +3033,17 @@ | |
| 3033 | ** SETTING: backoffice-nodelay boolean default=on |
| 3034 | ** If backoffice-nodelay is true, then the backoffice processing |
| 3035 | ** will never invoke sleep(). If it has nothing useful to do, |
| 3036 | ** it simply exits. |
| 3037 | */ |
| 3038 | /* |
| 3039 | ** SETTING: backoffice-logfile width=40 |
| 3040 | ** If backoffice-logfile is not an empty string and is a valid |
| 3041 | ** filename, then a one-line message is appended to that file |
| 3042 | ** every time the backoffice runs. This can be used for debugging, |
| 3043 | ** to ensure that backoffice is running appropriately. |
| 3044 | */ |
| 3045 | /* |
| 3046 | ** SETTING: binary-glob width=40 versionable block-text |
| 3047 | ** The VALUE of this setting is a comma or newline-separated list of |
| 3048 | ** GLOB patterns that should be treated as binary files |
| 3049 | ** for committing and merging purposes. Example: *.jpg |
| 3050 |