Fossil

Check-in [effdadad]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Enhancements to unix-domain socket support for "fossil server": (1) Change the command-line option to "--socket-name FILENAME" for creating the unix socket. (It was formerly --unix-socket.) (2) Add new command-line options "--socket-mode MODE" and "--socket-owner USER" or "... USER:GROUP" to set permissions and ownership on the new socket. (3) Attempt to unlink the socket from the filesystem upon exit.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: effdadadd0cbef4ef987e87b51d8962f64575753e3561a9161c77f3d1347ca68
User & Date: drh 2024-08-06 20:39:34.515
Context
2024-08-06
22:35
The new zebra-striped table styling in the default skin was not targeting /wiki docs, only .wiki embedded docs, leaving them unstyled. ... (check-in: 072a8609 user: wyoung tags: trunk)
20:39
Enhancements to unix-domain socket support for "fossil server": (1) Change the command-line option to "--socket-name FILENAME" for creating the unix socket. (It was formerly --unix-socket.) (2) Add new command-line options "--socket-mode MODE" and "--socket-owner USER" or "... USER:GROUP" to set permissions and ownership on the new socket. (3) Attempt to unlink the socket from the filesystem upon exit. ... (check-in: effdadad user: drh tags: trunk)
20:36
Get the build working on Windows again. ... (Closed-Leaf check-in: d474c95d user: drh tags: unix-sockets)
2024-08-05
20:23
Add the --unix-socket option to the "fossil server" command. ... (check-in: 7fc29021 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/cgi.c.
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
*/
#define HTTP_SERVER_LOCALHOST      0x0001     /* Bind to 127.0.0.1 only */
#define HTTP_SERVER_SCGI           0x0002     /* SCGI instead of HTTP */
#define HTTP_SERVER_HAD_REPOSITORY 0x0004     /* Was the repository open? */
#define HTTP_SERVER_HAD_CHECKOUT   0x0008     /* Was a checkout open? */
#define HTTP_SERVER_REPOLIST       0x0010     /* Allow repo listing */
#define HTTP_SERVER_NOFORK         0x0020     /* Do not call fork() */
#define HTTP_SERVER_UNIXDOMAINSOCK 0x0040     /* Use a unix-domain socket */

#endif /* INTERFACE */

/*
** Maximum number of child processes that we can have running
** at one time.  Set this to 0 for "no limit".
*/







|







2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
*/
#define HTTP_SERVER_LOCALHOST      0x0001     /* Bind to 127.0.0.1 only */
#define HTTP_SERVER_SCGI           0x0002     /* SCGI instead of HTTP */
#define HTTP_SERVER_HAD_REPOSITORY 0x0004     /* Was the repository open? */
#define HTTP_SERVER_HAD_CHECKOUT   0x0008     /* Was a checkout open? */
#define HTTP_SERVER_REPOLIST       0x0010     /* Allow repo listing */
#define HTTP_SERVER_NOFORK         0x0020     /* Do not call fork() */
#define HTTP_SERVER_UNIXSOCKET     0x0040     /* Use a unix-domain socket */

#endif /* INTERFACE */

/*
** Maximum number of child processes that we can have running
** at one time.  Set this to 0 for "no limit".
*/
2522
2523
2524
2525
2526
2527
2528
2529


2530
2531
2532
2533
2534
2535

2536



2537

2538
2539
2540









2541




2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559

2560
2561
2562
2563
2564
2565
2566







2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
  struct sockaddr_in inaddr;   /* The socket address */
  struct sockaddr_un uxaddr;   /* The address for unix-domain sockets */
  int opt = 1;                 /* setsockopt flag */
  int rc;                      /* Result code from system calls */
  int iPort = mnPort;          /* Port to try to use */

  while( iPort<=mxPort ){
    if( flags & HTTP_SERVER_UNIXDOMAINSOCK ){


      memset(&uxaddr, 0, sizeof(uxaddr));
      if( strlen(zIpAddr)>sizeof(uxaddr.sun_path) ){
        fossil_fatal("name of unix-domain socket too big: %s\n"
                     "max size: %d\n", zIpAddr, (int)sizeof(uxaddr.sun_path));
      }
      if( unlink(zIpAddr)==-1 && errno!=ENOENT ){

        fossil_fatal("Cannot remove existing file at %s\n", zIpAddr);



      }

      uxaddr.sun_family = AF_UNIX;
      strncpy(uxaddr.sun_path, zIpAddr, sizeof(uxaddr.sun_path)-1);
      listener = socket(AF_UNIX, SOCK_STREAM, 0);









    }else{




      memset(&inaddr, 0, sizeof(inaddr));
      inaddr.sin_family = AF_INET;
      if( zIpAddr ){
        inaddr.sin_addr.s_addr = inet_addr(zIpAddr);
        if( inaddr.sin_addr.s_addr == INADDR_NONE ){
          fossil_fatal("not a valid IP address: %s", zIpAddr);
        }
      }else if( flags & HTTP_SERVER_LOCALHOST ){
        inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
      }else{
        inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
      }
      inaddr.sin_port = htons(iPort);
      listener = socket(AF_INET, SOCK_STREAM, 0);
    }
    if( listener<0 ){
      iPort++;
      continue;

    }

    /* if we can't terminate nicely, at least allow the socket to be reused */
    setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
  
    if( flags & HTTP_SERVER_UNIXDOMAINSOCK ){
      rc = bind(listener, (struct sockaddr*)&uxaddr, sizeof(uxaddr));







    }else{
      rc = bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr));
    }
    if( rc<0 ){
      close(listener);
      iPort++;
      continue;
    }
    break;
  }
  if( iPort>mxPort ){
    if( flags & HTTP_SERVER_UNIXDOMAINSOCK ){
      fossil_fatal("unable to listen on unix-domain socket %s", zIpAddr);
    }else if( mnPort==mxPort ){
      fossil_fatal("unable to open listening socket on port %d", mnPort);
    }else{
      fossil_fatal("unable to open listening socket on any"
                   " port in the range %d..%d", mnPort, mxPort);
    }
  }
  if( iPort>mxPort ) return 1;
  listen(listener,10);
  if( flags & HTTP_SERVER_UNIXDOMAINSOCK ){
    fossil_print("Listening for %s requests on unix-domain socket %s\n",
       (flags & HTTP_SERVER_SCGI)!=0 ? "SCGI" :
          g.httpUseSSL?"TLS-encrypted HTTPS":"HTTP",  zIpAddr);
  }else{
    fossil_print("Listening for %s requests on TCP port %d\n",
       (flags & HTTP_SERVER_SCGI)!=0 ? "SCGI" :
          g.httpUseSSL?"TLS-encrypted HTTPS":"HTTP",  iPort);
  }
  fflush(stdout);
  if( zBrowser && (flags & HTTP_SERVER_UNIXDOMAINSOCK)==0 ){
    assert( strstr(zBrowser,"%d")!=0 );
    zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
#if defined(__CYGWIN__)
    /* On Cygwin, we can do better than "echo" */
    if( fossil_strncmp(zBrowser, "echo ", 5)==0 ){
      wchar_t *wUrl = fossil_utf8_to_unicode(zBrowser+5);
      wUrl[wcslen(wUrl)-2] = 0; /* Strip terminating " &" */







|
>
>

|
|
|

|
>
|
>
>
>
|
>

|

>
>
>
>
>
>
>
>
>
|
>
>
>
>














<
|
|
|
>





|

>
>
>
>
>
>
>











|
|









|
|

|






|







2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575

2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
  struct sockaddr_in inaddr;   /* The socket address */
  struct sockaddr_un uxaddr;   /* The address for unix-domain sockets */
  int opt = 1;                 /* setsockopt flag */
  int rc;                      /* Result code from system calls */
  int iPort = mnPort;          /* Port to try to use */

  while( iPort<=mxPort ){
    if( flags & HTTP_SERVER_UNIXSOCKET ){
      /* Initialize a Unix socket named g.zSockName */
      assert( g.zSockName!=0 );
      memset(&uxaddr, 0, sizeof(uxaddr));
      if( strlen(g.zSockName)>sizeof(uxaddr.sun_path) ){
        fossil_fatal("name of unix socket too big: %s\nmax size: %d\n",
                     g.zSockName, (int)sizeof(uxaddr.sun_path));
      }
      if( file_isdir(g.zSockName, ExtFILE)!=0 ){
        if( !file_issocket(g.zSockName) ){
          fossil_fatal("cannot name socket \"%s\" because another object"
                       " with that name already exists", g.zSockName);
        }else{
          unlink(g.zSockName);
        }
      }
      uxaddr.sun_family = AF_UNIX;
      strncpy(uxaddr.sun_path, g.zSockName, sizeof(uxaddr.sun_path)-1);
      listener = socket(AF_UNIX, SOCK_STREAM, 0);
      if( listener<0 ){
        fossil_fatal("unable to create a unix socket named %s",
                     g.zSockName);
      }
      /* Set the access permission for the new socket.  Default to 0660.
      ** But use an alternative specified by --socket-mode if available.
      ** Do this before bind() to avoid a race condition. */
      if( g.zSockMode ){
        file_set_mode(g.zSockName, listener, g.zSockMode, 0);
      }else{
        file_set_mode(g.zSockName, listener, "0660", 1);
      }
    }else{
      /* Initialize a TCP/IP socket on port iPort */
      memset(&inaddr, 0, sizeof(inaddr));
      inaddr.sin_family = AF_INET;
      if( zIpAddr ){
        inaddr.sin_addr.s_addr = inet_addr(zIpAddr);
        if( inaddr.sin_addr.s_addr == INADDR_NONE ){
          fossil_fatal("not a valid IP address: %s", zIpAddr);
        }
      }else if( flags & HTTP_SERVER_LOCALHOST ){
        inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
      }else{
        inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
      }
      inaddr.sin_port = htons(iPort);
      listener = socket(AF_INET, SOCK_STREAM, 0);

      if( listener<0 ){
        iPort++;
        continue;
      }
    }

    /* if we can't terminate nicely, at least allow the socket to be reused */
    setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
  
    if( flags & HTTP_SERVER_UNIXSOCKET ){
      rc = bind(listener, (struct sockaddr*)&uxaddr, sizeof(uxaddr));
      /* Set the owner of the socket if requested by --socket-owner.  This
      ** must wait until after bind(), after the filesystem object has been
      ** created.  See https://lkml.org/lkml/2004/11/1/84 and
      ** https://fossil-scm.org/forum/forumpost/7517680ef9684c57 */
      if( g.zSockOwner ){
        file_set_owner(g.zSockName, listener, g.zSockOwner);
      }
    }else{
      rc = bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr));
    }
    if( rc<0 ){
      close(listener);
      iPort++;
      continue;
    }
    break;
  }
  if( iPort>mxPort ){
    if( flags & HTTP_SERVER_UNIXSOCKET ){
      fossil_fatal("unable to listen on unix socket %s", zIpAddr);
    }else if( mnPort==mxPort ){
      fossil_fatal("unable to open listening socket on port %d", mnPort);
    }else{
      fossil_fatal("unable to open listening socket on any"
                   " port in the range %d..%d", mnPort, mxPort);
    }
  }
  if( iPort>mxPort ) return 1;
  listen(listener,10);
  if( flags & HTTP_SERVER_UNIXSOCKET ){
    fossil_print("Listening for %s requests on unix socket %s\n",
       (flags & HTTP_SERVER_SCGI)!=0 ? "SCGI" :
          g.httpUseSSL?"TLS-encrypted HTTPS":"HTTP",  g.zSockName);
  }else{
    fossil_print("Listening for %s requests on TCP port %d\n",
       (flags & HTTP_SERVER_SCGI)!=0 ? "SCGI" :
          g.httpUseSSL?"TLS-encrypted HTTPS":"HTTP",  iPort);
  }
  fflush(stdout);
  if( zBrowser && (flags & HTTP_SERVER_UNIXSOCKET)==0 ){
    assert( strstr(zBrowser,"%d")!=0 );
    zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
#if defined(__CYGWIN__)
    /* On Cygwin, we can do better than "echo" */
    if( fossil_strncmp(zBrowser, "echo ", 5)==0 ){
      wchar_t *wUrl = fossil_utf8_to_unicode(zBrowser+5);
      wUrl[wcslen(wUrl)-2] = 0; /* Strip terminating " &" */
Changes to src/file.c.
32
33
34
35
36
37
38


39
40
41
42
43
44
45
*/
#ifdef _WIN32
# include <direct.h>
# include <windows.h>
# include <sys/utime.h>
#else
# include <sys/time.h>


#endif

#if INTERFACE

/* Many APIs take an eFType argument which must be one of ExtFILE, RepoFILE,
** or SymFILE.
**







>
>







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
*/
#ifdef _WIN32
# include <direct.h>
# include <windows.h>
# include <sys/utime.h>
#else
# include <sys/time.h>
# include <pwd.h>
# include <grp.h>
#endif

#if INTERFACE

/* Many APIs take an eFType argument which must be one of ExtFILE, RepoFILE,
** or SymFILE.
**
227
228
229
230
231
232
233










234
235
236
237
238
239
240
/*
** Return TRUE if the named file is an ordinary file.  Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isfile(const char *zFilename, int eFType){
  return getStat(zFilename, eFType) ? 0 : S_ISREG(fx.fileStat.st_mode);
}











/*
** Create a symbolic link named zLinkFile that points to zTargetFile.
**
** If allow-symlinks is off, create an ordinary file named zLinkFile
** with the name of zTargetFile as its content.
**/







>
>
>
>
>
>
>
>
>
>







229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/*
** Return TRUE if the named file is an ordinary file.  Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isfile(const char *zFilename, int eFType){
  return getStat(zFilename, eFType) ? 0 : S_ISREG(fx.fileStat.st_mode);
}

/*
** Return TRUE if zFilename is a socket.
*/
int file_issocket(const char *zFilename){
  if( getStat(zFilename, ExtFILE) ){
    return 0;  /* stat() failed.  Return false. */
  }
  return S_ISSOCK(fx.fileStat.st_mode);
}

/*
** Create a symbolic link named zLinkFile that points to zTargetFile.
**
** If allow-symlinks is off, create an ordinary file named zLinkFile
** with the name of zTargetFile as its content.
**/
711
712
713
714
715
716
717






















































718
719
720
721
722
723
724
  iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
  zFile = g.argv[2];
  file_set_mtime(zFile, iMTime);
  iMTime = file_mtime(zFile, RepoFILE);
  zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
  fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
}























































/*
** Delete a file.
**
** If zFilename is a symbolic link, then it is the link itself that is
** removed, not the object that zFilename points to.
**







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
  iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
  zFile = g.argv[2];
  file_set_mtime(zFile, iMTime);
  iMTime = file_mtime(zFile, RepoFILE);
  zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
  fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
}

/*
** Change access permissions on a file.
*/
void file_set_mode(const char *zFN, int fd, const char *zMode, int bNoErr){
#if !defined(_WIN32)
  mode_t m;
  char *zEnd = 0;
  m = strtol(zMode, &zEnd, 0);
  if( (zEnd[0] || fchmod(fd, m)) && !bNoErr ){
    fossil_fatal("cannot change permissions on %s to \"%s\"",
                 zFN, zMode);
  }
#endif
}

/* Change the owner of a file to zOwner.  zOwner can be of the form
** USER:GROUP.
*/
void file_set_owner(const char *zFN, int fd, const char *zOwner){
#if !defined(_WIN32)
  const char *zGrp;
  const char *zUsr = zOwner;
  struct passwd *pw;
  struct group *grp;
  uid_t uid = -1;
  gid_t gid = -1;
  zGrp = strchr(zUsr, ':');
  if( zGrp ){
    int n = (int)(zGrp - zUsr);
    zUsr = fossil_strndup(zUsr, n);
    zGrp++;
  }
  pw = getpwnam(zUsr);
  if( pw==0 ){
    fossil_fatal("no such user: \"%s\"", zUsr);
  }
  uid = pw->pw_uid;
  if( zGrp ){
    grp = getgrnam(zGrp);
    if( grp==0 ){
      fossil_fatal("no such group: \"%s\"", zGrp);
    }
    gid = grp->gr_gid;
  }
  if( chown(zFN, uid, gid) ){
    fossil_fatal("cannot change ownership of %s to %s",zFN, zOwner);
  }
  if( zOwner!=zUsr ){
    fossil_free((char*)zUsr);
  }
#endif
}


/*
** Delete a file.
**
** If zFilename is a symbolic link, then it is the link itself that is
** removed, not the object that zFilename points to.
**
1512
1513
1514
1515
1516
1517
1518

1519
1520
1521
1522
1523
1524
1525
  z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime);
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z);
  fossil_free(z);
  fossil_print("  file_mtime(ExtFILE)    = %s\n", zBuf);
  fossil_print("  file_mode(ExtFILE)     = 0%o\n", file_mode(zPath,ExtFILE));
  fossil_print("  file_isfile(ExtFILE)   = %d\n", file_isfile(zPath,ExtFILE));
  fossil_print("  file_isdir(ExtFILE)    = %d\n", file_isdir(zPath,ExtFILE));

  if( reset ) resetStat();
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zPath,RepoFILE));
  fossil_print("  file_size(RepoFILE)    = %s\n", zBuf);
  iMtime = file_mtime(zPath,RepoFILE);
  z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime);
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z);
  fossil_free(z);







>







1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
  z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime);
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z);
  fossil_free(z);
  fossil_print("  file_mtime(ExtFILE)    = %s\n", zBuf);
  fossil_print("  file_mode(ExtFILE)     = 0%o\n", file_mode(zPath,ExtFILE));
  fossil_print("  file_isfile(ExtFILE)   = %d\n", file_isfile(zPath,ExtFILE));
  fossil_print("  file_isdir(ExtFILE)    = %d\n", file_isdir(zPath,ExtFILE));
  fossil_print("  file_issocket()        = %d\n", file_issocket(zPath));
  if( reset ) resetStat();
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zPath,RepoFILE));
  fossil_print("  file_size(RepoFILE)    = %s\n", zBuf);
  iMtime = file_mtime(zPath,RepoFILE);
  z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime);
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z);
  fossil_free(z);
Changes to src/main.c.
236
237
238
239
240
241
242



243
244
245
246
247
248
249
#endif
  int useLocalauth;       /* No login required if from 127.0.0.1 */
  int noPswd;             /* Logged in without password (on 127.0.0.1) */
  int userUid;            /* Integer user id */
  int isHuman;            /* True if access by a human, not a spider or bot */
  int comFmtFlags;        /* Zero or more "COMMENT_PRINT_*" bit flags, should be
                          ** accessed through get_comment_format(). */




  /* Information used to populate the RCVFROM table */
  int rcvid;              /* The rcvid.  0 if not yet defined. */
  char *zIpAddr;          /* The remote IP address */
  char *zNonce;           /* The nonce used for login */

  /* permissions available to current user */







>
>
>







236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#endif
  int useLocalauth;       /* No login required if from 127.0.0.1 */
  int noPswd;             /* Logged in without password (on 127.0.0.1) */
  int userUid;            /* Integer user id */
  int isHuman;            /* True if access by a human, not a spider or bot */
  int comFmtFlags;        /* Zero or more "COMMENT_PRINT_*" bit flags, should be
                          ** accessed through get_comment_format(). */
  const char *zSockName;  /* Name of the unix-domain socket file */
  const char *zSockMode;  /* File permissions for unix-domain socket */
  const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */

  /* Information used to populate the RCVFROM table */
  int rcvid;              /* The rcvid.  0 if not yet defined. */
  char *zIpAddr;          /* The remote IP address */
  char *zNonce;           /* The nonce used for login */

  /* permissions available to current user */
389
390
391
392
393
394
395





396
397
398
399
400
401
402
  ** exiting the process.  This issue does not impact 64-bit Windows.
  */
  unloadTcl(g.interp, &g.tcl);
#endif
#ifdef FOSSIL_ENABLE_JSON
  cson_value_free(g.json.gc.v);
  memset(&g.json, 0, sizeof(g.json));





#endif
  free(g.zErrMsg);
  if(g.db){
    db_close(0);
  }
  manifest_clear_cache();
  content_clear_cache(1);







>
>
>
>
>







392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
  ** exiting the process.  This issue does not impact 64-bit Windows.
  */
  unloadTcl(g.interp, &g.tcl);
#endif
#ifdef FOSSIL_ENABLE_JSON
  cson_value_free(g.json.gc.v);
  memset(&g.json, 0, sizeof(g.json));
#endif
#if !defined(_WIN32)
  if( g.zSockName && file_issocket(g.zSockName) ){
    unlink(g.zSockName);
  }
#endif
  free(g.zErrMsg);
  if(g.db){
    db_close(0);
  }
  manifest_clear_cache();
  content_clear_cache(1);
1505
1506
1507
1508
1509
1510
1511

1512
1513
1514
1515
1516
1517

1518
1519

1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531












1532
1533
1534
1535
1536
1537
1538
static char *enter_chroot_jail(const char *zRepo, int noJail){
#if !defined(_WIN32)
  if( getuid()==0 ){
    int i;
    struct stat sStat;
    Blob dir;
    char *zDir;

    if( g.db!=0 ){
      db_close(1);
    }

    file_canonical_name(zRepo, &dir, 0);
    zDir = blob_str(&dir);

    if( !noJail ){
      if( file_isdir(zDir, ExtFILE)==1 ){

        if( g.zRepositoryName ){
          size_t n = strlen(zDir);
          Blob repo;
          file_canonical_name(g.zRepositoryName, &repo, 0);
          zRepo = blob_str(&repo);
          if( strncmp(zRepo, zDir, n)!=0 ){
            fossil_fatal("repo %s not under chroot dir %s", zRepo, zDir);
          }
          zRepo += n;
          if( *zRepo == '\0' ) zRepo = "/";
        }else {
          zRepo = "/";












        }
        if( file_chdir(zDir, 1) ){
          fossil_panic("unable to chroot into %s", zDir);
        }
      }else{
        for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
        if( zDir[i]!='/' ) fossil_fatal("bad repository name: %s", zRepo);







>






>


>

<



|


|



>
>
>
>
>
>
>
>
>
>
>
>







1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531

1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
static char *enter_chroot_jail(const char *zRepo, int noJail){
#if !defined(_WIN32)
  if( getuid()==0 ){
    int i;
    struct stat sStat;
    Blob dir;
    char *zDir;
    size_t nDir;
    if( g.db!=0 ){
      db_close(1);
    }

    file_canonical_name(zRepo, &dir, 0);
    zDir = blob_str(&dir);
    nDir = blob_size(&dir);
    if( !noJail ){
      if( file_isdir(zDir, ExtFILE)==1 ){
        /* Translate the repository name to the new root */
        if( g.zRepositoryName ){

          Blob repo;
          file_canonical_name(g.zRepositoryName, &repo, 0);
          zRepo = blob_str(&repo);
          if( strncmp(zRepo, zDir, nDir)!=0 ){
            fossil_fatal("repo %s not under chroot dir %s", zRepo, zDir);
          }
          zRepo += nDir;
          if( *zRepo == '\0' ) zRepo = "/";
        }else {
          zRepo = "/";
        }
        /* If a unix socket is defined, try to translate its name into
        ** the new root so that it can be delete by atexit().  If unable,
        ** just zero out the socket name. */
        if( g.zSockName ){
          if( strncmp(g.zSockName, zDir, nDir)==0
           && g.zSockName[nDir]=='/'
          ){
            g.zSockName += nDir;
          }else{
            g.zSockName = 0;
          }
        }
        if( file_chdir(zDir, 1) ){
          fossil_panic("unable to chroot into %s", zDir);
        }
      }else{
        for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
        if( zDir[i]!='/' ) fossil_fatal("bad repository name: %s", zRepo);
3183
3184
3185
3186
3187
3188
3189

3190
3191
3192




3193
3194
3195
3196
3197
3198
3199
**   -p|--page PAGE      Start "ui" on PAGE.  ex: --page "timeline?y=ci"
**   --pkey FILE         Read the private key used for TLS from FILE
**   -P|--port [IP:]PORT  Listen on the given IP (optional) and port
**   --repolist          If REPOSITORY is dir, URL "/" lists repos
**   --scgi              Accept SCGI rather than HTTP
**   --skin LABEL        Use override skin LABEL, or the site's default skin if
**                       LABEL is an empty string.

**   --th-trace          Trace TH1 execution (for debugging purposes)
**   --unix-socket NAME  Listen on unix-domain socket NAME rather than on a
**                       TCP/IP port.




**   --usepidkey         Use saved encryption key from parent process.  This is
**                       only necessary when using SEE on Windows or Linux.
**
** See also: [[cgi]], [[http]], [[winsrv]]
*/
void cmd_webserver(void){
  int iPort, mxPort;        /* Range of TCP ports allowed */







>
|
|
|
>
>
>
>







3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
**   -p|--page PAGE      Start "ui" on PAGE.  ex: --page "timeline?y=ci"
**   --pkey FILE         Read the private key used for TLS from FILE
**   -P|--port [IP:]PORT  Listen on the given IP (optional) and port
**   --repolist          If REPOSITORY is dir, URL "/" lists repos
**   --scgi              Accept SCGI rather than HTTP
**   --skin LABEL        Use override skin LABEL, or the site's default skin if
**                       LABEL is an empty string.
**   --socket-mode MODE  File permissions to set for the unix socket created
**                       by the --socket-name option.
**   --socket-name NAME  Use a unix-domain socket called NAME instead of a
**                       TCP/IP socket.
**   --socket-owner USR  Try to set the owner of the unix socket to USR.
**                       USR can be of the form USER:GROUP to set both
**                       user and group.
**   --th-trace          Trace TH1 execution (for debugging purposes)
**   --usepidkey         Use saved encryption key from parent process.  This is
**                       only necessary when using SEE on Windows or Linux.
**
** See also: [[cgi]], [[http]], [[winsrv]]
*/
void cmd_webserver(void){
  int iPort, mxPort;        /* Range of TCP ports allowed */
3283
3284
3285
3286
3287
3288
3289
3290


3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
  }
  g.zCkoutAlias = find_option("ckout-alias",0,1);
  g.zMainMenuFile = find_option("mainmenu",0,1);
  if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
    fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
  }
  if( find_option("acme",0,0)!=0 ) g.fAllowACME = 1;
  zIpAddr = (char*)find_option("unix-socket",0,1);


  if( zIpAddr ){
#if defined(_WIN32)
    fossil_fatal("unix sockets are not supported on Windows");
#endif
    if( zPort ){
      fossil_fatal("cannot specify a port number for a unix socket");
    }
    if( isUiCmd && !fNoBrowser ){
      fossil_fatal("cannot start a web-browser on a unix socket");
    }
    flags |= HTTP_SERVER_UNIXDOMAINSOCK;
  }

  /* Undocumented option:  --debug-nofork
  **
  ** This sets the HTTP_SERVER_NOFORK flag, which causes only the
  ** very first incoming TCP/IP connection to be processed.  Used for
  ** debugging, since debugging across a fork() can be tricky







|
>
>
|









|







3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
  }
  g.zCkoutAlias = find_option("ckout-alias",0,1);
  g.zMainMenuFile = find_option("mainmenu",0,1);
  if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
    fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
  }
  if( find_option("acme",0,0)!=0 ) g.fAllowACME = 1;
  g.zSockMode = find_option("socket-mode",0,1);
  g.zSockName = find_option("socket-name",0,1);
  g.zSockOwner = find_option("socket-owner",0,1);
  if( g.zSockName ){
#if defined(_WIN32)
    fossil_fatal("unix sockets are not supported on Windows");
#endif
    if( zPort ){
      fossil_fatal("cannot specify a port number for a unix socket");
    }
    if( isUiCmd && !fNoBrowser ){
      fossil_fatal("cannot start a web-browser on a unix socket");
    }
    flags |= HTTP_SERVER_UNIXSOCKET;
  }

  /* Undocumented option:  --debug-nofork
  **
  ** This sets the HTTP_SERVER_NOFORK flag, which causes only the
  ** very first incoming TCP/IP connection to be processed.  Used for
  ** debugging, since debugging across a fork() can be tricky
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
    fossil_free(zBrowserCmd);
    return;
  }
  if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
  if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  db_close(1);
#if !defined(_WIN32)
  if( getpid()==1 ){
    /* Modern kernels suppress SIGTERM to PID 1 to prevent root from
    ** rebooting the system by nuking the init system.  The only way
    ** Fossil becomes that PID 1 is when it's running solo in a Linux
    ** container or similar, so we do want to exit immediately, to
    ** allow the container to shut down quickly.
    **
    ** This has to happen ahead of the other signal() calls below.







|







3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
    fossil_free(zBrowserCmd);
    return;
  }
  if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
  if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  db_close(1);
#if !defined(_WIN32)
  if( 1 ){
    /* Modern kernels suppress SIGTERM to PID 1 to prevent root from
    ** rebooting the system by nuking the init system.  The only way
    ** Fossil becomes that PID 1 is when it's running solo in a Linux
    ** container or similar, so we do want to exit immediately, to
    ** allow the container to shut down quickly.
    **
    ** This has to happen ahead of the other signal() calls below.
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
  /* Start up an HTTP server
  */
  fossil_setenv("SERVER_SOFTWARE", "fossil version " RELEASE_VERSION
                " " MANIFEST_VERSION " " MANIFEST_DATE);
#if !defined(_WIN32)
  /* Unix implementation */
  if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){
    fossil_fatal("unable to listen on TCP socket %d", iPort);
  }
  /* For the parent process, the cgi_http_server() command above never
  ** returns (except in the case of an error).  Instead, for each incoming
  ** client connection, a child process is created, file descriptors 0
  ** and 1 are bound to that connection, and the child returns.
  **
  ** So, when control reaches this point, we are running as a







|







3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
  /* Start up an HTTP server
  */
  fossil_setenv("SERVER_SOFTWARE", "fossil version " RELEASE_VERSION
                " " MANIFEST_VERSION " " MANIFEST_DATE);
#if !defined(_WIN32)
  /* Unix implementation */
  if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){
    fossil_fatal("unable to listen on CGI socket");
  }
  /* For the parent process, the cgi_http_server() command above never
  ** returns (except in the case of an error).  Instead, for each incoming
  ** client connection, a child process is created, file descriptors 0
  ** and 1 are bound to that connection, and the child returns.
  **
  ** So, when control reaches this point, we are running as a