Fossil

Check-in [7fc2902126]
Login

Check-in [7fc2902126]

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

Overview
Comment:Add the --unix-socket option to the "fossil server" command.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 7fc2902126d43f87f897a46d12209f5b04d71d2cb50acd08ffad94c9b0c6aba6
User & Date: drh 2024-08-05 20:23:40.923
Context
2024-08-06
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: effdadadd0 user: drh tags: trunk)
15:00
Change --unix-socket to --socket-name. Add --socket-mode and --socket-owner. Mostly working, accept that --socket-owner seemingly has no effect, even though the fchown() return 0. There is currently a debugging printf() in that line of code. This is an experimental check-in. ... (check-in: 9f71e5cc02 user: drh tags: unix-sockets)
2024-08-05
20:23
Add the --unix-socket option to the "fossil server" command. ... (check-in: 7fc2902126 user: drh tags: trunk)
15:10
Replace the JavaScript-based side-by-side diff view with a CSS Grid, as discussed in [forum:93398561d3986c41|forum post 93398561d3986c41]. ... (check-in: 71e9ca7869 user: stephan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/cgi.c.
68
69
70
71
72
73
74

75
76
77
78
79
80

81
82
83
84
85
86
87
# if !defined(_WIN32_WINNT)
#  define _WIN32_WINNT 0x0501
# endif
# include <winsock2.h>
# include <ws2tcpip.h>
#else
# include <sys/socket.h>

# include <netinet/in.h>
# include <arpa/inet.h>
# include <sys/times.h>
# include <sys/time.h>
# include <sys/wait.h>
# include <sys/select.h>

#endif
#ifdef __EMX__
  typedef int socklen_t;
#endif
#include <time.h>
#include <stdio.h>
#include <stdlib.h>







>






>







68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# if !defined(_WIN32_WINNT)
#  define _WIN32_WINNT 0x0501
# endif
# include <winsock2.h>
# include <ws2tcpip.h>
#else
# include <sys/socket.h>
# include <sys/un.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <sys/times.h>
# include <sys/time.h>
# include <sys/wait.h>
# include <sys/select.h>
# include <errno.h>
#endif
#ifdef __EMX__
  typedef int socklen_t;
#endif
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
2473
2474
2475
2476
2477
2478
2479

2480
2481
2482
2483
2484
2485
2486
*/
#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() */


#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_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".
*/
2513
2514
2515
2516
2517
2518
2519

2520

2521
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
  int nRequest = 0;            /* Number of requests handled so far */
  fd_set readfds;              /* Set of file descriptors for select() */
  socklen_t lenaddr;           /* Length of the inaddr structure */
  int child;                   /* PID of the child process */
  int nchildren = 0;           /* Number of child processes */
  struct timeval delay;        /* How long to wait inside select() */
  struct sockaddr_in inaddr;   /* The socket address */

  int opt = 1;                 /* setsockopt flag */

  int iPort = mnPort;

  while( iPort<=mxPort ){













    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( bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr))<0 ){


      close(listener);
      iPort++;
      continue;
    }
    break;
  }
  if( iPort>mxPort ){


    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);





  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 ){
    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 " &" */







>

>
|


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







|
>
>
>
|
>
>







>
>
|








>
>
>
>
>
|
|
|
>

|







2516
2517
2518
2519
2520
2521
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
  int nRequest = 0;            /* Number of requests handled so far */
  fd_set readfds;              /* Set of file descriptors for select() */
  socklen_t lenaddr;           /* Length of the inaddr structure */
  int child;                   /* PID of the child process */
  int nchildren = 0;           /* Number of child processes */
  struct timeval delay;        /* How long to wait inside select() */
  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 " &" */
Changes to src/main.c.
3184
3185
3186
3187
3188
3189
3190


3191
3192
3193
3194
3195
3196
3197
**   --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)


**   --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 */







>
>







3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
**   --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
  const char *zChRoot;      /* Use for chroot instead of repository path */
  int noJail;               /* Do not enter the chroot jail */
  const char *zTimeout = 0; /* Max runtime of any single HTTP request */
#endif
  int allowRepoList;         /* List repositories on URL "/" */
  const char *zAltBase;      /* Argument to the --baseurl option */
  const char *zFileGlob;     /* Static content must match this */
  char *zIpAddr = 0;         /* Bind to this IP address */
  int fCreate = 0;           /* The --create flag */
  int fNoBrowser = 0;        /* Do not auto-launch web-browser */
  const char *zInitPage = 0; /* Start on this page.  --page option */
  int findServerArg = 2;     /* argv index for find_server_repository() */
  char *zRemote = 0;         /* Remote host on which to run "fossil ui" */
  const char *zJsMode;       /* The --jsmode parameter */
  const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */







|







3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
  const char *zChRoot;      /* Use for chroot instead of repository path */
  int noJail;               /* Do not enter the chroot jail */
  const char *zTimeout = 0; /* Max runtime of any single HTTP request */
#endif
  int allowRepoList;         /* List repositories on URL "/" */
  const char *zAltBase;      /* Argument to the --baseurl option */
  const char *zFileGlob;     /* Static content must match this */
  char *zIpAddr = 0;         /* Bind to this IP address or UN socket */
  int fCreate = 0;           /* The --create flag */
  int fNoBrowser = 0;        /* Do not auto-launch web-browser */
  const char *zInitPage = 0; /* Start on this page.  --page option */
  int findServerArg = 2;     /* argv index for find_server_repository() */
  char *zRemote = 0;         /* Remote host on which to run "fossil ui" */
  const char *zJsMode;       /* The --jsmode parameter */
  const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
3281
3282
3283
3284
3285
3286
3287













3288
3289
3290
3291
3292
3293
3294
  }
  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;














  /* 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
  */







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







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
3309
  }
  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
  */