LCOV - code coverage report
Current view: top level - clients/mapilib - connect_unix.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 71 102 69.6 %
Date: 2024-12-20 20:06:10 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : #include "monetdb_config.h"
      14             : #include "stream.h"           /* include before mapi.h */
      15             : #include "stream_socket.h"
      16             : #include "mapi.h"
      17             : #include "mapi_prompt.h"
      18             : #include "mcrypt.h"
      19             : #include "matomic.h"
      20             : #include "mstring.h"
      21             : #include "mutils.h"
      22             : 
      23             : #include "mapi_intern.h"
      24             : 
      25             : #define MAX_SCAN (24)
      26             : 
      27             : MapiMsg
      28           2 : scan_unix_sockets(Mapi mid)
      29             : {
      30           2 :         struct {
      31             :                 int port;
      32             :                 int priority;
      33             :         } candidates[MAX_SCAN];
      34           2 :         int ncandidates = 0;
      35           2 :         DIR *dir = NULL;
      36           2 :         struct dirent *entry;
      37             : 
      38           2 :         const char *sockdir = msetting_string(mid->settings, MP_SOCKDIR);
      39           2 :         size_t len = strlen(sockdir);
      40           2 :         char *namebuf = malloc(len + 50);
      41           2 :         if (namebuf == NULL)
      42           0 :                 return mapi_setError(mid, "malloc failed", __func__, MERROR);
      43           2 :         strcpy(namebuf, sockdir);
      44           2 :         strcpy(namebuf + len, "/.s.monetdb.PORTXXXXX");
      45           2 :         char *put_port_here = strrchr(namebuf, 'P');
      46             : 
      47           2 :         msettings *original = mid->settings;
      48           2 :         mid->settings = NULL;  // invalid state, will fix it before use and on return
      49             : 
      50           2 :         mapi_log_record(mid, "CONN", "Scanning %s for Unix domain sockets", sockdir);
      51             : 
      52             :         // Make a list of Unix domain sockets in /tmp
      53           2 :         uid_t me = getuid();
      54           2 :         dir = opendir(sockdir);
      55           2 :         if (dir) {
      56         122 :                 while (ncandidates < MAX_SCAN && (entry = readdir(dir)) != NULL) {
      57         120 :                         const char *basename = entry->d_name;
      58         120 :                         if (strncmp(basename, ".s.monetdb.", 11) != 0 || basename[11] == '\0' || strlen(basename) > 20)
      59         118 :                                 continue;
      60             : 
      61           2 :                         char *end;
      62           2 :                         long port = strtol(basename + 11, &end, 10);
      63           2 :                         if (port < 1 || port > 65535 || *end)
      64           0 :                                 continue;
      65             : 
      66           2 :                         sprintf(put_port_here, "%ld", port);
      67           2 :                         struct stat st;
      68           2 :                         if (stat(namebuf, &st) < 0 || !S_ISSOCK(st.st_mode))
      69           0 :                                 continue;
      70             : 
      71           2 :                         candidates[ncandidates].port = port;
      72           2 :                         candidates[ncandidates++].priority = st.st_uid == me ? 0 : 1;
      73             :                 }
      74           2 :                 closedir(dir);
      75             :         }
      76             : 
      77           2 :         mapi_log_record(mid, "CONN", "Found %d Unix domain sockets", ncandidates);
      78             : 
      79             :         // Try those owned by us first, then all others
      80           6 :         for (int round = 0; round < 2; round++) {
      81           8 :                 for (int i = 0; i < ncandidates; i++) {
      82           4 :                         if (candidates[i].priority != round)
      83           2 :                                 continue;
      84             : 
      85           2 :                         assert(!mid->connected);
      86           2 :                         assert(mid->settings == NULL);
      87           2 :                         mid->settings = msettings_clone(original);
      88           2 :                         if (!mid->settings) {
      89           0 :                                 mid->settings = original;
      90           0 :                                 free(namebuf);
      91           0 :                                 return mapi_setError(mid, "malloc failed", __func__, MERROR);
      92             :                         }
      93           2 :                         msettings_error errmsg = msetting_set_long(mid->settings, MP_PORT, candidates[i].port);
      94           2 :                         char *allocated_errmsg = NULL;
      95           2 :                         if (!errmsg && !msettings_validate(mid->settings, &allocated_errmsg)) {
      96           0 :                                 errmsg = allocated_errmsg;
      97             :                         }
      98           2 :                         if (errmsg) {
      99           0 :                                 mapi_setError(mid, errmsg, __func__, MERROR);
     100           0 :                                 free(allocated_errmsg);
     101           0 :                                 free(namebuf);
     102           0 :                                 msettings_destroy(mid->settings);
     103           0 :                                 mid->settings = original;
     104           0 :                                 return MERROR;
     105             :                         }
     106           2 :                         MapiMsg msg = establish_connection(mid);
     107           2 :                         if (msg == MOK) {
     108             :                                 // do not restore original
     109           0 :                                 msettings_destroy(original);
     110           0 :                                 free(namebuf);
     111           0 :                                 return MOK;
     112             :                         } else {
     113           2 :                                 msettings_destroy(mid->settings);
     114           2 :                                 mid->settings = NULL;
     115             :                                 // now we're ready to try another one
     116             :                         }
     117             :                 }
     118             :         }
     119             : 
     120           2 :         free(namebuf);
     121           2 :         assert(mid->settings == NULL);
     122           2 :         mid->settings = original;
     123           2 :         mapi_log_record(mid, "CONN", "All %d Unix domain sockets failed. Falling back to TCP", ncandidates);
     124             :         return MERROR;
     125             : }
     126             : 
     127             : 
     128             : MapiMsg
     129          83 : connect_socket_unix(Mapi mid)
     130             : {
     131          83 :         const char *sockname = msettings_connect_unix(mid->settings);
     132          83 :         assert (*sockname != '\0');
     133          83 :         long timeout = msetting_long(mid->settings, MP_CONNECT_TIMEOUT);
     134             : 
     135          83 :         mapi_log_record(mid, "CONN", "Connecting to Unix domain socket %s with timeout %ld", sockname, timeout);
     136             : 
     137          83 :         struct sockaddr_un userver;
     138          83 :         if (strlen(sockname) >= sizeof(userver.sun_path)) {
     139           0 :                 return mapi_printError(mid, __func__, MERROR, "path name '%s' too long", sockname);
     140             :         }
     141             : 
     142             :         // Create the socket, taking care of CLOEXEC and SNDTIMEO
     143             : 
     144             : #ifdef SOCK_CLOEXEC
     145          83 :         int s = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
     146             : #else
     147             :         int s = socket(PF_UNIX, SOCK_STREAM, 0);
     148             : #endif
     149          83 :         if (s == INVALID_SOCKET) {
     150           0 :                 return mapi_printError(
     151             :                         mid, __func__, MERROR,
     152           0 :                         "could not create Unix domain socket '%s': %s", sockname, strerror(errno));
     153             :         }
     154             : #if !defined(SOCK_CLOEXEC) && defined(HAVE_FCNTL)
     155             :         (void) fcntl(s, F_SETFD, FD_CLOEXEC);
     156             : #endif
     157             : 
     158          83 :         if (timeout > 0) {
     159           0 :                 struct timeval tv = {
     160           0 :                         .tv_sec = timeout / 1000,
     161           0 :                         .tv_usec = timeout % 1000,
     162             :                 };
     163           0 :                 if (
     164           0 :                         setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == SOCKET_ERROR
     165           0 :                         || setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == SOCKET_ERROR
     166             :                 ) {
     167           0 :                         closesocket(s);
     168           0 :                         return mapi_printError(
     169             :                                 mid, __func__, MERROR,
     170           0 :                                 "could not set connect timeout: %s", strerror(errno));
     171             :                 }
     172             :         }
     173             : 
     174             :         // Attempt to connect
     175             : 
     176          83 :         userver = (struct sockaddr_un) {
     177             :                 .sun_family = AF_UNIX,
     178             :         };
     179          83 :         strcpy_len(userver.sun_path, sockname, sizeof(userver.sun_path));
     180             : 
     181          83 :         if (connect(s, (struct sockaddr *) &userver, sizeof(struct sockaddr_un)) == SOCKET_ERROR) {
     182          12 :                 closesocket(s);
     183          12 :                 return mapi_printError(
     184             :                         mid, __func__, MERROR,
     185          12 :                         "connect to Unix domain socket '%s' failed: %s", sockname, strerror(errno));
     186             :         }
     187             : 
     188             :         // Send an initial zero (not NUL) to let the server know we're not passing a file
     189             :         // descriptor.
     190             : 
     191          71 :         ssize_t n = send(s, "0", 1, 0);
     192          71 :         if (n < 1) {
     193             :                 // used to be if n < 0 but this makes more sense
     194           0 :                 closesocket(s);
     195           0 :                 return mapi_printError(
     196             :                         mid, __func__, MERROR,
     197           0 :                         "could not send initial '0' on Unix domain socket: %s", strerror(errno));
     198             :         }
     199             : 
     200          71 :         return wrap_socket(mid, s);
     201             : }

Generated by: LCOV version 1.14