LCOV - code coverage report
Current view: top level - app/fdctl - utility.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 39 166 23.5 %
Date: 2024-11-13 11:58:15 Functions: 1 11 9.1 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "utility.h"
       3             : 
       4             : #include <unistd.h>
       5             : #include <stdio.h>
       6             : #include <stdarg.h>
       7             : #include <string.h> /* for explicit_bzero */
       8             : #include <time.h>
       9             : #include <fcntl.h>
      10             : #include <sched.h>
      11             : #include <dirent.h>
      12             : 
      13             : #include <sys/mman.h> /* for mprotect */
      14             : #include <sys/stat.h>
      15             : #include <sys/socket.h>
      16             : #include <sys/mount.h>
      17             : #include <sys/syscall.h>
      18             : #include <linux/netlink.h>
      19             : #include <linux/rtnetlink.h>
      20             : #include <linux/unistd.h>
      21             : 
      22             : static int namespace_original_fd = 0;
      23             : 
      24             : void
      25           0 : enter_network_namespace( const char * interface ) {
      26           0 :   char path[ PATH_MAX ];
      27           0 :   FD_TEST( fd_cstr_printf_check( path, PATH_MAX, NULL, "/var/run/netns/%s", interface ) );
      28             : 
      29           0 :   if( FD_LIKELY( !namespace_original_fd ) ) {
      30           0 :     namespace_original_fd = open( "/proc/self/ns/net", O_RDONLY | O_CLOEXEC );
      31           0 :     if( FD_UNLIKELY( namespace_original_fd < 0 ) )
      32           0 :       FD_LOG_ERR(( "failed to open /proc/self/ns/net (%i-%s)", errno, fd_io_strerror( errno ) ));
      33           0 :   }
      34             : 
      35           0 :   int fd = open( path, O_RDONLY | O_CLOEXEC );
      36           0 :   if( FD_UNLIKELY( fd < 0 ) ) FD_LOG_ERR(( "failed to open `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      37           0 :   if( FD_UNLIKELY( setns( fd, CLONE_NEWNET ) ) )
      38           0 :     FD_LOG_ERR(( "failed to enter network namespace `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      39           0 :   int ret = close( fd );
      40           0 :   if( FD_UNLIKELY( ret ) ) FD_LOG_ERR(( "enter_network_namespace %d (%i-%s)", ret, errno, fd_io_strerror( errno ) ));
      41           0 : }
      42             : 
      43             : void
      44           0 : close_network_namespace_original_fd( void ) {
      45           0 :   int ret = close( namespace_original_fd );
      46           0 :   if( FD_UNLIKELY( ret ) ) FD_LOG_ERR(( "leave_network_namespace %d (%i-%s)", ret, errno, fd_io_strerror( errno ) ));
      47           0 : }
      48             : 
      49             : void
      50           0 : leave_network_namespace( void ) {
      51           0 :   if( FD_UNLIKELY( !namespace_original_fd ) ) return;
      52             : 
      53           0 :   if( FD_UNLIKELY( setns( namespace_original_fd, CLONE_NEWNET ) ) )
      54           0 :     FD_LOG_ERR(( "failed to enter original network namespace `%d` (%i-%s)",
      55           0 :                  namespace_original_fd, errno, fd_io_strerror( errno ) ));
      56             : 
      57           0 :   int ret = close( namespace_original_fd );
      58           0 :   if( FD_UNLIKELY( ret ) ) FD_LOG_ERR(( "leave_network_namespace %d (%i-%s)", ret, errno, fd_io_strerror( errno ) ));
      59           0 :   namespace_original_fd = 0;
      60           0 : }
      61             : 
      62             : void
      63           0 : exit_group( int status ) {
      64           0 :   syscall( SYS_exit_group, status );
      65           0 : }
      66             : 
      67             : void
      68             : mkdir_all( const char * _path,
      69             :            uid_t        uid,
      70           0 :            gid_t        gid ) {
      71           0 :   char path[ PATH_MAX + 1 ] = {0};
      72           0 :   strncpy( path, _path, PATH_MAX );
      73             : 
      74           0 :   char * p = path;
      75           0 :   if( FD_LIKELY( *p == '/' ) ) p++;
      76           0 :   while( FD_LIKELY( *p ) ) {
      77           0 :     if( FD_UNLIKELY( *p == '/' ) ) {
      78           0 :       *p = '\0';
      79           0 :       int error = mkdir( path, 0777 );
      80           0 :       if( FD_UNLIKELY( error && errno != EEXIST ) )
      81           0 :         FD_LOG_ERR(( "mkdir( `%s` ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ) );
      82           0 :       if( FD_LIKELY( !error ) ) {
      83             :         /* only take ownership if we succeeded in creating (did not exist) */
      84           0 :         if( FD_UNLIKELY( chown( path, uid, gid ) ) )
      85           0 :           FD_LOG_ERR(( "chown `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      86           0 :         if( FD_UNLIKELY( chmod( path, S_IRUSR | S_IWUSR | S_IXUSR ) ) )
      87           0 :           FD_LOG_ERR(( "chmod `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      88           0 :       }
      89             : 
      90           0 :       *p = '/';
      91           0 :     }
      92           0 :     p++;
      93           0 :   }
      94             : 
      95           0 :   int error = mkdir( path, 0777 );
      96           0 :   if( FD_UNLIKELY( error && errno != EEXIST ) )
      97           0 :     FD_LOG_ERR(( "mkdir( `%s` ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ) );
      98           0 :   if( FD_LIKELY( !error ) ) {
      99             :     /* only take ownership if we succeeded in creating (did not exist) */
     100           0 :     if( FD_UNLIKELY( chown( path, uid, gid ) ) )
     101           0 :       FD_LOG_ERR(( "chown `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     102           0 :     if( FD_UNLIKELY( chmod( path, S_IRUSR | S_IWUSR | S_IXUSR ) ) )
     103           0 :       FD_LOG_ERR(( "chmod `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     104           0 :   }
     105           0 : }
     106             : 
     107             : void
     108             : rmtree( char const * path,
     109           0 :         int          remove_root ) {
     110           0 :   DIR * dir = opendir( path );
     111           0 :   if( FD_UNLIKELY( !dir ) ) {
     112           0 :     if( errno == ENOENT ) return;
     113           0 :     FD_LOG_ERR(( "opendir `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     114           0 :   }
     115             : 
     116           0 :   struct dirent * entry;
     117           0 :   errno = 0;
     118           0 :   while(( entry = readdir( dir ) )) {
     119           0 :     if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
     120             : 
     121           0 :     char path1[ PATH_MAX ];
     122           0 :     FD_TEST( fd_cstr_printf_check( path1, PATH_MAX, NULL, "%s/%s", path, entry->d_name ) );
     123             : 
     124           0 :     struct stat st;
     125           0 :     if( FD_UNLIKELY( lstat( path1, &st ) ) ) {
     126           0 :       if( FD_LIKELY( errno == ENOENT ) ) continue;
     127           0 :       FD_LOG_ERR(( "stat `%s` failed (%i-%s)", path1, errno, fd_io_strerror( errno ) ));
     128           0 :     }
     129             : 
     130           0 :     if( FD_UNLIKELY( S_ISDIR( st.st_mode ) ) ) {
     131           0 :       rmtree( path1, 1 );
     132           0 :     } else {
     133           0 :       if( FD_UNLIKELY( unlink( path1 ) && errno != ENOENT ) )
     134           0 :         FD_LOG_ERR(( "unlink `%s` failed (%i-%s)", path1, errno, fd_io_strerror( errno ) ));
     135           0 :     }
     136           0 :   }
     137             : 
     138           0 :   if( FD_UNLIKELY( errno && errno != ENOENT ) ) FD_LOG_ERR(( "readdir `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     139             : 
     140           0 :   if( FD_LIKELY( remove_root ) ) {
     141           0 :     if( FD_UNLIKELY( rmdir( path ) ) ) FD_LOG_ERR(( "rmdir `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     142           0 :   }
     143           0 :   if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     144           0 : }
     145             : 
     146             : uint
     147             : read_uint_file( char const * path,
     148           0 :                 char const * errmsg_enoent ) {
     149           0 :   FILE * fp = fopen( path, "r" );
     150           0 :   if( FD_UNLIKELY( !fp ) ) {
     151           0 :     if( FD_LIKELY( errno==ENOENT ) ) FD_LOG_ERR(( "%s fopen failed `%s` (%i-%s)", errmsg_enoent, path, errno, fd_io_strerror( errno ) ));
     152           0 :     else                             FD_LOG_ERR(( "fopen failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     153           0 :   }
     154             : 
     155           0 :   uint value = 0U;
     156           0 :   if( FD_UNLIKELY( 1!=fscanf( fp, "%u\n", &value ) ) ) FD_LOG_ERR(( "failed to read uint from `%s`", path ));
     157           0 :   if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_ERR(( "fclose failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     158           0 :   return value;
     159           0 : }
     160             : 
     161             : void
     162             : write_uint_file( char const * path,
     163           0 :                  uint         value ) {
     164           0 :   FILE * fp = fopen( path, "w" );
     165           0 :   if( FD_UNLIKELY( !fp ) ) FD_LOG_ERR(( "fopen failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     166           0 :   if( FD_UNLIKELY( fprintf( fp, "%u\n", value ) <= 0 ) ) FD_LOG_ERR(( "fprintf failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     167           0 :   if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_ERR(( "fclose failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     168           0 : }
     169             : 
     170             : int
     171           3 : internet_routing_interface( void ) {
     172           3 :   int sock = socket( AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
     173           3 :   if( FD_UNLIKELY( sock < 0 ) )
     174           0 :     FD_LOG_ERR(( "error finding default interface, socket(AF_INET,SOCK_DGRAM,0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     175             : 
     176           3 :   struct {
     177           3 :     struct nlmsghdr nlh;
     178           3 :     struct rtmsg rt;
     179           3 :     char buf[8192];
     180           3 :   } request;
     181             : 
     182           3 :   memset(&request, 0, sizeof(request));
     183           3 :   request.nlh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct rtmsg));
     184           3 :   request.nlh.nlmsg_flags = NLM_F_REQUEST;
     185           3 :   request.nlh.nlmsg_type  = RTM_GETROUTE;
     186           3 :   request.rt.rtm_family   = AF_INET;
     187           3 :   request.rt.rtm_dst_len  = 32;
     188             : 
     189           3 :   struct rtattr *rta = (struct rtattr *)( ( (char *)&request ) + NLMSG_ALIGN( request.nlh.nlmsg_len ) );
     190           3 :   rta->rta_len          = RTA_LENGTH(4);
     191           3 :   rta->rta_type         = RTA_DST;
     192           3 :   request.nlh.nlmsg_len = NLMSG_ALIGN( request.nlh.nlmsg_len ) + (uint)RTA_LENGTH( 4 );
     193             : 
     194           3 :   unsigned int ip = (8 << 24) | (8 << 16) | (8 << 8) | 8;
     195           3 :   fd_memcpy( RTA_DATA( rta ), &ip, 4 );
     196             : 
     197           3 :   if( FD_UNLIKELY( send( sock, &request, request.nlh.nlmsg_len, 0 ) < 0 ) )
     198           0 :     FD_LOG_ERR(( "error finding default interface, send() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     199             : 
     200           3 :   char response[ 8192 ];
     201           3 :   long len = recv( sock, response, sizeof(response), 0 );
     202           3 :   if( FD_UNLIKELY( len == sizeof( response ) ) )
     203           0 :     FD_LOG_ERR(( "error finding default interface, response too large" ));
     204             : 
     205           3 :   struct nlmsghdr *nlh;
     206           3 :   int result = -1;
     207           3 :   for( nlh = (struct nlmsghdr *)response; NLMSG_OK( nlh, len ); nlh = NLMSG_NEXT( nlh, len ) ) {
     208           3 :       struct rtmsg *rt = NLMSG_DATA( nlh );
     209             : 
     210           3 :       struct rtattr *rta = RTM_RTA( rt );
     211           3 :       uint rtl = (uint)RTM_PAYLOAD( nlh );
     212             : 
     213          21 :       for (; RTA_OK( rta, rtl ); rta = RTA_NEXT( rta, rtl ) ) {
     214          21 :           if (rta->rta_type == RTA_OIF) {
     215           3 :             result = *(int *)RTA_DATA(rta);
     216           3 :           }
     217          21 :       }
     218           3 :   }
     219             : 
     220           3 :   if( FD_UNLIKELY( close( sock ) ) )
     221           0 :     FD_LOG_ERR(( "error finding default interface, close() socket failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     222             : 
     223           3 :   return result;
     224           3 : }
     225             : 
     226             : /* FIXME: USE FD_LOG_SLEEP / FD_LOG_WAIT_UNTIL */
     227             : void
     228           0 : nanosleep1( uint secs, uint nanos ) {
     229           0 :   struct timespec ts = { .tv_sec = secs, .tv_nsec = nanos };
     230           0 :   struct timespec rem;
     231           0 :   while( FD_UNLIKELY( nanosleep( &ts, &rem ) ) ) {
     232           0 :     if( FD_LIKELY( errno == EINTR ) ) ts = rem;
     233           0 :     else FD_LOG_ERR(( "nanosleep failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     234           0 :   }
     235           0 : }
     236             : 
     237             : void
     238           0 : current_executable_path( char path[ static PATH_MAX ] ) {
     239           0 :   long count = readlink( "/proc/self/exe", path, PATH_MAX );
     240           0 :   if( FD_UNLIKELY( count < 0 ) ) FD_LOG_ERR(( "readlink(/proc/self/exe) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     241           0 :   if( FD_UNLIKELY( count >= PATH_MAX ) ) FD_LOG_ERR(( "readlink(/proc/self/exe) returned truncated path" ));
     242           0 :   path[ count ] = '\0';
     243           0 : }

Generated by: LCOV version 1.14