LCOV - code coverage report
Current view: top level - clients/mapilib - connect_unix.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 17 91 18.7 %
Date: 2024-04-26 00:35:57 Functions: 1 2 50.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           0 : scan_unix_sockets(Mapi mid)
      29             : {
      30           0 :         struct {
      31             :                 int port;
      32             :                 int priority;
      33             :         } candidates[MAX_SCAN];
      34           0 :         int ncandidates = 0;
      35           0 :         DIR *dir = NULL;
      36           0 :         struct dirent *entry;
      37             : 
      38           0 :         const char *sockdir = msetting_string(mid->settings, MP_SOCKDIR);
      39           0 :         size_t len = strlen(sockdir);
      40           0 :         char *namebuf = malloc(len + 50);
      41           0 :         if (namebuf == NULL)
      42           0 :                 return mapi_setError(mid, "malloc failed", __func__, MERROR);
      43           0 :         strcpy(namebuf, sockdir);
      44           0 :         strcpy(namebuf + len, "/.s.monetdb.PORTXXXXX");
      45           0 :         char *put_port_here = strrchr(namebuf, 'P');
      46             : 
      47           0 :         msettings *original = mid->settings;
      48           0 :         mid->settings = NULL;  // invalid state, will fix it before use and on return
      49             : 
      50           0 :         mapi_log_record(mid, "CONN", "Scanning %s for Unix domain sockets", sockdir);
      51             : 
      52             :         // Make a list of Unix domain sockets in /tmp
      53           0 :         uid_t me = getuid();
      54           0 :         dir = opendir(sockdir);
      55           0 :         if (dir) {
      56           0 :                 while (ncandidates < MAX_SCAN && (entry = readdir(dir)) != NULL) {
      57           0 :                         const char *basename = entry->d_name;
      58           0 :                         if (strncmp(basename, ".s.monetdb.", 11) != 0 || basename[11] == '\0' || strlen(basename) > 20)
      59           0 :                                 continue;
      60             : 
      61           0 :                         char *end;
      62           0 :                         long port = strtol(basename + 11, &end, 10);
      63           0 :                         if (port < 1 || port > 65535 || *end)
      64           0 :                                 continue;
      65             : 
      66           0 :                         sprintf(put_port_here, "%ld", port);
      67           0 :                         struct stat st;
      68           0 :                         if (stat(namebuf, &st) < 0 || !S_ISSOCK(st.st_mode))
      69           0 :                                 continue;
      70             : 
      71           0 :                         candidates[ncandidates].port = port;
      72           0 :                         candidates[ncandidates++].priority = st.st_uid == me ? 0 : 1;
      73             :                 }
      74           0 :                 closedir(dir);
      75             :         }
      76             : 
      77           0 :         mapi_log_record(mid, "CONN", "Found %d Unix domain sockets", ncandidates);
      78             : 
      79             :         // Try those owned by us first, then all others
      80           0 :         for (int round = 0; round < 2; round++) {
      81           0 :                 for (int i = 0; i < ncandidates; i++) {
      82           0 :                         if (candidates[i].priority != round)
      83           0 :                                 continue;
      84             : 
      85           0 :                         assert(!mid->connected);
      86           0 :                         assert(mid->settings == NULL);
      87           0 :                         mid->settings = msettings_clone(original);
      88           0 :                         if (!mid->settings) {
      89           0 :                                 mid->settings = original;
      90           0 :                                 free(namebuf);
      91           0 :                                 return mapi_setError(mid, "malloc failed", __func__, MERROR);
      92             :                         }
      93           0 :                         msettings_error errmsg = msetting_set_long(mid->settings, MP_PORT, candidates[i].port);
      94           0 :                         char *allocated_errmsg = NULL;
      95           0 :                         if (!errmsg && !msettings_validate(mid->settings, &allocated_errmsg)) {
      96           0 :                                 errmsg = allocated_errmsg;
      97             :                         }
      98           0 :                         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           0 :                         MapiMsg msg = establish_connection(mid);
     107           0 :                         if (msg == MOK) {
     108             :                                 // do not restore original
     109           0 :                                 msettings_destroy(original);
     110           0 :                                 free(namebuf);
     111           0 :                                 return MOK;
     112             :                         } else {
     113           0 :                                 msettings_destroy(mid->settings);
     114           0 :                                 mid->settings = NULL;
     115             :                                 // now we're ready to try another one
     116             :                         }
     117             :                 }
     118             :         }
     119             : 
     120           0 :         free(namebuf);
     121           0 :         assert(mid->settings == NULL);
     122           0 :         mid->settings = original;
     123           0 :         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          68 : connect_socket_unix(Mapi mid)
     130             : {
     131          68 :         const char *sockname = msettings_connect_unix(mid->settings);
     132          68 :         assert (*sockname != '\0');
     133             : 
     134          68 :         mapi_log_record(mid, "CONN", "Connecting to Unix domain socket %s", sockname);
     135             : 
     136          68 :         struct sockaddr_un userver;
     137          68 :         if (strlen(sockname) >= sizeof(userver.sun_path)) {
     138           0 :                 return mapi_printError(mid, __func__, MERROR, "path name '%s' too long", sockname);
     139             :         }
     140             : 
     141             :         // Create the socket, taking care of CLOEXEC
     142             : 
     143             : #ifdef SOCK_CLOEXEC
     144          68 :         int s = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
     145             : #else
     146             :         int s = socket(PF_UNIX, SOCK_STREAM, 0);
     147             : #endif
     148          68 :         if (s == INVALID_SOCKET) {
     149           0 :                 return mapi_printError(
     150             :                         mid, __func__, MERROR,
     151           0 :                         "could not create Unix domain socket '%s': %s", sockname, strerror(errno));
     152             :         }
     153             : #if !defined(SOCK_CLOEXEC) && defined(HAVE_FCNTL)
     154             :         (void) fcntl(s, F_SETFD, FD_CLOEXEC);
     155             : #endif
     156             : 
     157             :         // Attempt to connect
     158             : 
     159          68 :         userver = (struct sockaddr_un) {
     160             :                 .sun_family = AF_UNIX,
     161             :         };
     162          68 :         strcpy_len(userver.sun_path, sockname, sizeof(userver.sun_path));
     163             : 
     164          68 :         if (connect(s, (struct sockaddr *) &userver, sizeof(struct sockaddr_un)) == SOCKET_ERROR) {
     165           2 :                 closesocket(s);
     166           2 :                 return mapi_printError(
     167             :                         mid, __func__, MERROR,
     168           2 :                         "connect to Unix domain socket '%s' failed: %s", sockname, strerror(errno));
     169             :         }
     170             : 
     171             :         // Send an initial zero (not NUL) to let the server know we're not passing a file
     172             :         // descriptor.
     173             : 
     174          66 :         ssize_t n = send(s, "0", 1, 0);
     175          66 :         if (n < 1) {
     176             :                 // used to be if n < 0 but this makes more sense
     177           0 :                 closesocket(s);
     178           0 :                 return mapi_printError(
     179             :                         mid, __func__, MERROR,
     180           0 :                         "could not send initial '0' on Unix domain socket: %s", strerror(errno));
     181             :         }
     182             : 
     183          66 :         return wrap_socket(mid, s);
     184             : }

Generated by: LCOV version 1.14