Line data Source code
1 : #define _GNU_SOURCE
2 : #include "fd_sys_util.h"
3 :
4 : #include <pwd.h>
5 : #include <errno.h>
6 : #include <fcntl.h>
7 : #include <stdlib.h> /* getenv */
8 : #include <time.h>
9 : #include <unistd.h>
10 : #include <sys/syscall.h>
11 : #include <sys/mman.h>
12 : #include <sys/wait.h>
13 :
14 : void __attribute__((noreturn))
15 0 : fd_sys_util_exit_group( int code ) {
16 0 : syscall( SYS_exit_group, code );
17 0 : for(;;);
18 0 : }
19 :
20 : int
21 : fd_sys_util_nanosleep( uint secs,
22 0 : uint nanos ) {
23 0 : struct timespec ts = { .tv_sec = secs, .tv_nsec = nanos };
24 0 : struct timespec rem;
25 0 : while( FD_UNLIKELY( -1==nanosleep( &ts, &rem ) ) ) {
26 0 : if( FD_LIKELY( errno==EINTR ) ) ts = rem;
27 0 : else return -1;
28 0 : }
29 0 : return 0;
30 0 : }
31 :
32 : int
33 : fd_sys_util_modprobe( char const * module,
34 0 : int is_dry_run ) {
35 0 : pid_t pid = fork();
36 0 : if( FD_UNLIKELY( -1==pid ) ) {
37 0 : FD_LOG_WARNING(( "fork() for modprobe failed (%i-%s)", errno, fd_io_strerror( errno ) ));
38 0 : return -1;
39 0 : }
40 0 : if( 0==pid ) {
41 0 : int null_fd = open( "/dev/null", O_RDWR );
42 0 : if( FD_UNLIKELY( -1==null_fd ) ) {
43 0 : FD_LOG_WARNING(( "open(/dev/null) for modprobe failed (%i-%s)", errno, fd_io_strerror( errno ) ));
44 0 : _exit( 1 );
45 0 : }
46 0 : if( FD_UNLIKELY( -1==dup2( null_fd, STDIN_FILENO ) ||
47 0 : ( is_dry_run && -1==dup2( null_fd, STDOUT_FILENO ) ) ) ) {
48 0 : FD_LOG_WARNING(( "dup2() for modprobe failed (%i-%s)", errno, fd_io_strerror( errno ) ));
49 0 : _exit( 1 );
50 0 : }
51 0 : if( null_fd!=STDIN_FILENO &&
52 0 : !( is_dry_run && null_fd==STDOUT_FILENO ) ) close( null_fd );
53 :
54 0 : char * argv[] = { "modprobe", "--quiet", (char *)module, NULL, NULL };
55 0 : if( is_dry_run ) {
56 0 : argv[2] = "--dry-run";
57 0 : argv[3] = (char *)module;
58 0 : }
59 0 : char * const envp[] = { NULL };
60 0 : execve( "/sbin/modprobe", argv, envp );
61 0 : FD_LOG_WARNING(( "execve(/sbin/modprobe) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
62 0 : _exit( 1 );
63 0 : }
64 :
65 0 : int status;
66 0 : while( FD_UNLIKELY( -1==waitpid( pid, &status, 0 ) ) ) {
67 0 : if( errno==EINTR ) continue;
68 0 : FD_LOG_WARNING(( "waitpid() for modprobe failed (%i-%s)", errno, fd_io_strerror( errno ) ));
69 0 : return -1;
70 0 : }
71 0 : return WIFEXITED( status ) && 0==WEXITSTATUS( status ) ? 0 : -1;
72 0 : }
73 :
74 : char const *
75 0 : fd_sys_util_login_user( void ) {
76 0 : char * name = getenv( "SUDO_USER" );
77 0 : if( FD_UNLIKELY( name ) ) return name;
78 :
79 0 : name = getenv( "LOGNAME" );
80 0 : if( FD_LIKELY( name ) ) return name;
81 :
82 0 : name = getenv( "USER" );
83 0 : if( FD_LIKELY( name ) ) return name;
84 :
85 0 : name = getenv( "LNAME" );
86 0 : if( FD_LIKELY( name ) ) return name;
87 :
88 0 : name = getenv( "USERNAME" );
89 0 : if( FD_LIKELY( name ) ) return name;
90 :
91 0 : name = getlogin();
92 0 : if( FD_UNLIKELY( !name && (errno==ENXIO || errno==ENOTTY) ) ) return NULL;
93 0 : else if( FD_UNLIKELY( !name ) ) FD_LOG_ERR(( "getlogin failed (%i-%s)", errno, fd_io_strerror( errno ) ));
94 0 : return name;
95 0 : }
96 :
97 : int
98 : fd_sys_util_user_to_uid( char const * user,
99 : uint * uid,
100 0 : uint * gid ) {
101 0 : uint * results = mmap( NULL, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0 );
102 0 : if( FD_UNLIKELY( results==MAP_FAILED ) ) return -1;
103 :
104 0 : results[ 0 ] = UINT_MAX;
105 0 : results[ 1 ] = UINT_MAX;
106 :
107 : /* This is extremely unfortunate. We just want to call getpwnam but
108 : on various glibc it can open `/var/lib/sss/mc/passwd` and then not
109 : close it. We could go and find this file descriptor and close it
110 : for the library, but that is a bit of a hack. Instead we fork a
111 : new process to call getpwnam and then exit.
112 :
113 : We could try just reading /etc/passwd here instead, but the glibc
114 : getpwnam implementation does a lot of things we need, including
115 : potentially reading from NCSD or SSSD. */
116 :
117 0 : pid_t pid = fork();
118 0 : if( FD_UNLIKELY( -1==pid ) ) {
119 0 : munmap( results, 4096 );
120 0 : return -1;
121 0 : }
122 :
123 0 : if( FD_LIKELY( !pid ) ) {
124 0 : char buf[ 16384 ];
125 0 : struct passwd pwd;
126 0 : struct passwd * result;
127 0 : int error = getpwnam_r( user, &pwd, buf, sizeof(buf), &result );
128 0 : if( FD_UNLIKELY( error ) ) {
129 0 : if( FD_LIKELY( error==ENOENT || error==ESRCH ) ) {
130 0 : FD_LOG_WARNING(( "configuration file wants firedancer to run as user `%s` but it does not exist", user ));
131 0 : fd_sys_util_exit_group( 1 );
132 0 : } else {
133 0 : FD_LOG_WARNING(( "could not get user id for `%s` (%i-%s)", user, errno, fd_io_strerror( errno ) ));
134 0 : fd_sys_util_exit_group( 1 );
135 0 : }
136 0 : }
137 0 : if( FD_UNLIKELY( !result ) ) {
138 0 : FD_LOG_WARNING(( "configuration file wants firedancer to run as user `%s` but it does not exist", user ));
139 0 : fd_sys_util_exit_group( 1 );
140 0 : }
141 :
142 0 : results[ 0 ] = pwd.pw_uid;
143 0 : results[ 1 ] = pwd.pw_gid;
144 0 : fd_sys_util_exit_group( 0 );
145 0 : } else {
146 0 : int wstatus;
147 0 : if( FD_UNLIKELY( -1==waitpid( pid, &wstatus, 0 ) ) ) return -1;
148 0 : if( FD_UNLIKELY( WIFSIGNALED( wstatus ) ) ) {
149 0 : FD_LOG_WARNING(( "uid fetch process terminated by signal %i-%s", WTERMSIG( wstatus ), fd_io_strsignal( WTERMSIG( wstatus ) ) ));
150 0 : munmap( results, 4096 );
151 0 : errno = EINTR;
152 0 : return -1;
153 0 : }
154 0 : if( FD_UNLIKELY( WEXITSTATUS( wstatus ) ) ) {
155 0 : FD_LOG_WARNING(( "uid fetch process exited with status %i", WEXITSTATUS( wstatus ) ));
156 0 : munmap( results, 4096 );
157 0 : errno = EINTR;
158 0 : return -1;
159 0 : }
160 0 : }
161 :
162 0 : if( FD_UNLIKELY( results[ 0 ]==UINT_MAX || results[ 1 ]==UINT_MAX ) ) {
163 0 : munmap( results, 4096 );
164 0 : errno = ENOENT;
165 0 : return -1;
166 0 : }
167 :
168 0 : *uid = results[ 0 ];
169 0 : *gid = results[ 1 ];
170 :
171 0 : if( FD_UNLIKELY( -1==munmap( results, 4096 ) ) ) return -1;
172 :
173 0 : return 0;
174 0 : }
|