LCOV - code coverage report
Current view: top level - src - exec.c (source / functions) Hit Total Coverage
Test: deployctl-0.3.15.2.96a2d Code Coverage Lines: 101 369 27.4 %
Date: 2018-06-22 Functions: 7 15 46.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  exec.c
       3             :  Created by Danny Goossen, Gioxa Ltd on 4/3/17.
       4             : 
       5             :  MIT License
       6             : 
       7             :  Copyright (c) 2017 deployctl, Gioxa Ltd.
       8             : 
       9             :  Permission is hereby granted, free of charge, to any person obtaining a copy
      10             :  of this software and associated documentation files (the "Software"), to deal
      11             :  in the Software without restriction, including without limitation the rights
      12             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      13             :  copies of the Software, and to permit persons to whom the Software is
      14             :  furnished to do so, subject to the following conditions:
      15             : 
      16             :  The above copyright notice and this permission notice shall be included in all
      17             :  copies or substantial portions of the Software.
      18             : 
      19             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      20             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      21             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      22             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      23             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      24             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      25             :  SOFTWARE.
      26             : 
      27             :  */
      28             : 
      29             : 
      30             : 
      31             : #include "deployd.h"
      32             : 
      33             : #define PIPE_READ 0
      34             : #define PIPE_WRITE 1
      35             : 
      36             : struct script_control {
      37             :    const char *shell;           /* shell to be executed */
      38             :    const char *shellcommand;            /* command to be executed */
      39             :    int master;          /* pseudoterminal master file descriptor */
      40             :    int slave;           /* pseudoterminal slave file descriptor */
      41             :    pid_t child;
      42             :    #if !HAVE_LIBUTIL
      43             :       char *line;               /* terminal line */
      44             :    #else
      45             :       int close_pipe;
      46             :    #endif
      47             :    int childstatus;     /* child process exit value */
      48             :    //char ** envp;
      49             :    cJSON * env_vars;
      50             :    gid_t gid;
      51             :    uid_t uid;
      52             :    int timeout;
      53             : 
      54             :    unsigned int
      55             : change_ugid:1,
      56             : need_envp:1,
      57             : die:1;                  /* terminate program */
      58             : };
      59             : 
      60             : 
      61          92 : int socket_nonblocking (int sock)
      62             : {
      63             :    int flags;
      64             : 
      65          92 :    if(sock != INVALID_SOCKET)
      66             :    {
      67          92 :       flags = fcntl (sock, F_GETFL, 0);
      68          92 :       return fcntl (sock, F_SETFL, flags | O_NONBLOCK | O_CLOEXEC | SO_KEEPALIVE);//| O_NDELAY);
      69             :    }
      70             :    return -1;
      71             : 
      72             : }
      73             : /*
      74             :  * Set the socket to blocking -rjkaes
      75             :  */
      76           0 : int socket_blocking (int sock)
      77             : {
      78             :    int flags;
      79             : 
      80           0 :    if(sock != INVALID_SOCKET)
      81             :    {
      82           0 :       flags = fcntl (sock, F_GETFL, 0);
      83           0 :       return fcntl (sock, F_SETFL, (flags & ~O_NONBLOCK) | O_CLOEXEC);// | O_NDELAY);
      84             :    }
      85             :    return -1;
      86             : }
      87             : 
      88           0 : int socket_O_CLOEXEC (int sock)
      89             : {
      90             :    int flags;
      91             : 
      92           0 :    if(sock != INVALID_SOCKET)
      93             :    {
      94           0 :       flags = fcntl (sock, F_GETFL, 0);
      95           0 :       return fcntl (sock, F_SETFL, flags | O_CLOEXEC);
      96             :    }
      97             :    return -1;
      98             : 
      99             : }
     100             : /*------------------------------------------------------------------------
     101             :  * Helper functions for uid and gid
     102             :  *------------------------------------------------------------------------*/
     103             : 
     104           8 : int get_uid_gid_user(uidguid_t * uidguid,const char * user)
     105             : {
     106             :     struct passwd pwd;
     107             :     struct passwd *result;
     108             :     char *buf;
     109             :     size_t bufsize;
     110             :     int s;
     111             : 
     112           8 :     bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
     113           8 :     if (bufsize == -1)          /* Value was indeterminate */
     114           0 :         bufsize = 16384;        /* Should be more than enough */
     115             : 
     116           8 :     buf = malloc(bufsize);
     117           8 :     if (buf == NULL) { error("malloc pwd\n");return -1;}
     118             : 
     119           8 :     s = getpwnam_r(user, &pwd, buf, bufsize, &result);
     120           8 :     free(buf);
     121           8 :     if (result == NULL) {
     122           2 :         if (s == 0) error("user %s not found\n",user);
     123           0 :         else {errno = s; sock_error("getpwnam_r");}
     124             :         return 1;
     125             :     }
     126           6 :     uidguid->gid=pwd.pw_gid;
     127           6 :     uidguid->uid=pwd.pw_uid;
     128           6 :     return 0;
     129             : }
     130             : 
     131           4 : int get_gid(gid_t * gid,const char* user)
     132             : {
     133             :     uidguid_t uidguid;
     134           4 :     int ret=get_uid_gid_user(&uidguid,user);
     135           4 :     if (!ret)*gid=uidguid.gid;
     136           4 :     return ret;
     137             : }
     138           4 : int get_uid(uid_t * uid,const char*user)
     139             : {
     140             :     uidguid_t uidguid;
     141           4 :     int ret=get_uid_gid_user(&uidguid,user);
     142           4 :     if (!ret) *uid=uidguid.uid;
     143           4 :     return ret;
     144             : }
     145             : 
     146             : int exec_cmd_col(void * opaque,int cmd);
     147             : 
     148             : /*----------------------------------------------------------------------------------
     149             :  * int cmd_exec()
     150             :  * fork and executes the a command as per ((data_exchange_t *)opaque)->paramlist
     151             :  * and optional environment a per ((data_exchange_t *)opaque)->newenvp
     152             :  * * with gid and uid as per data_exchange.gid/uid
     153             :  * with umask according to umask
     154             :  *
     155             :  * blocking function with timeout while waiting for feedback and exit-code
     156             :  *
     157             :  * output's the cmd stdout and stderr in output_buff and returns exit code for client
     158             :  *-----------------------------------------------------------------------------------*/
     159          51 : int cmd_exec(void * opaque)
     160             : {
     161          51 :   return(exec_cmd_col(opaque,2));
     162             : }
     163             : 
     164             : /*----------------------------------------------------------------------------------
     165             :  * int cmd_write()
     166             :  * fork and executes a write file command as per
     167             :  * and optional environment a per ((data_exchange_t *)opaque)->newenvp
     168             :  * with gid and uid as per data_exchange.gid/uid
     169             :  * with umask according to umask
     170             :  *
     171             :  * blocking function with timeout while waiting for feedback and exit-code
     172             :  *
     173             :  * output's the cmd stdout and stderr in output_buff and returns exit code for client
     174             :  *-----------------------------------------------------------------------------------*/
     175          41 : int cmd_write(void * opaque)
     176             : {
     177          41 :   return(exec_cmd_col(opaque,1));
     178             : }
     179             : 
     180             : /*----------------------------------------------------------------------------------
     181             :  * int exec_col(void * opaque,int cmd)
     182             :  * fork and executes a wcmd_write or cmd_exec according to cmd
     183             :  * and optional environment a per ((data_exchange_t *)opaque)->newenvp
     184             :  * with gid and uid as per data_exchange.gid/uid
     185             :  * with umask according to umask
     186             :  * blocking function with timeout while waiting for feedback and exit-code
     187             :  *
     188             :  * output's the cmd stdout and stderr in output_buff and returns exit code for client
     189             :  *-----------------------------------------------------------------------------------*/
     190          92 : int exec_cmd_col(void * opaque,int cmd_choice)
     191             : {
     192          92 :    debug("start command exec\n");
     193             : 
     194          92 :     data_exchange_t * data_exchange=((data_exchange_t *)opaque);
     195             :     // feedback buffer
     196          92 :    int exitcode=0;
     197          92 :    struct trace_Struct *trace=((data_exchange_t *)opaque)->trace;
     198             : 
     199             :     // new envp
     200          92 :     cJSON * env_vars=data_exchange->env_json;
     201             : 
     202             :     // get the details for the command
     203          92 :     char ** paramlist =data_exchange->paramlist;
     204             : 
     205          92 :     int needenvp=data_exchange->needenvp;
     206          92 :     gid_t gid=data_exchange->gid;
     207          92 :     uid_t uid=data_exchange->uid;
     208             : 
     209             :     pid_t child_pid;
     210             : 
     211             :     int aStdoutPipe[2];
     212          92 :     if (pipe(aStdoutPipe) < 0) {sock_error("allocating pipe for child stdout redirect");Write_dyn_trace(trace, red,"System error\n");return(15);}
     213             :     // Fork_it
     214          92 :       fflush(stdout);
     215          92 :     child_pid = fork();
     216             : 
     217         184 :    if(child_pid == 0)
     218             :    {
     219             :       // The Child
     220             :       // redirect stdout
     221          92 :       if (dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1) {sock_error("redirecting stdout");exit(2);}
     222             :       // redirect stderr
     223          92 :       if (dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1) {sock_error("redirecting stderr");exit(3);}
     224             : 
     225          92 :       int fd = open("/dev/null", O_WRONLY);
     226          92 :       if (dup2( fd , STDIN_FILENO) == -1) {sock_error("redirecting stdin");exit(3);}
     227             :       // all these are for use by parent only, so close
     228             : 
     229          92 :       close(aStdoutPipe[PIPE_READ]);
     230          92 :       close(aStdoutPipe[PIPE_WRITE]);
     231             :       // set user to root and group to the requested user
     232             : 
     233          92 :       if (!data_exchange->current_user && setgid(gid)) { error("could not set gid\n") ; exit(4);}
     234          92 :       if (!data_exchange->current_user && setuid(uid)) { error("could not set uid\n") ; exit(5);}
     235             : 
     236             :       // user=7 rwx , group=7 r--, other: ---
     237          92 :       umask( S_IRWXO ); /* set newly created file permissions */
     238             : 
     239             :       //printf("%s : %s %s\n", paramlist[0],paramlist[1],paramlist[2]);
     240             : 
     241          92 :       if (cmd_choice==1)
     242             :       {
     243             :          // write to file
     244          41 :          FILE *f = fopen(paramlist[1], "w");
     245          41 :          if (f == NULL){ fprintf(stderr,"Error opening file : %s\n",strerror(errno));exit(8);}
     246             :          /* print some text */
     247             :          int retvalue=0;
     248             :          int errvalue=0;
     249          38 :          do{ retvalue=fprintf(f, "%s",paramlist[2]);} while ( retvalue==-1 && (errvalue=errno)== EINTR );
     250          38 :          if (retvalue<0) { fprintf(stderr,"Error io-command %s: %s\n", paramlist[0],strerror(errvalue)); exit (9); }
     251          38 :          if (fclose(f) <0 ) { fprintf(stderr,"Error io-command %s: %s\n", paramlist[0],strerror(errno));exit (10);}
     252          38 :          exit(0);
     253             :       }
     254             :       // LCOV_EXCL_START
     255             :       else if (cmd_choice==2)
     256             : 
     257             :       // does get tested, but no registration since we never exit here
     258             :       {
     259             :          if (needenvp && env_vars && cJSON_GetArraySize(env_vars)>0)
     260             :          {
     261             :             cJSON * pos=NULL;
     262             :             cJSON_ArrayForEach(pos, env_vars)
     263             :             {
     264             :                if (cJSON_IsString(pos))
     265             :                {
     266             :                   setenv(pos->string,pos->valuestring,1);
     267             :                   debug("env: %s=%s\n",pos->string,pos->valuestring);
     268             :                }
     269             :                else debug("skipped non string %s\n",pos->string);
     270             :             }
     271             :             //cJSON_Delete(env_vars);
     272             :             //env_vars=NULL;
     273             :          }
     274             : 
     275             :          {
     276             :             execvp(paramlist[0], paramlist);
     277             :             //printf("what's wrong here???\n");
     278             :          }
     279             :       // just for safety, should never come here
     280             :       _exit(6);
     281             :       } else exit(7);
     282             :    // LCOV_EXCL_STOP
     283             :    }
     284          92 :    else if (child_pid>0) // The Parent
     285             :    {
     286             :      // parent wait's and return result
     287             :      // close unused file descriptors, these are for child only
     288             : 
     289          92 :       close(aStdoutPipe[PIPE_WRITE]);
     290             :       char output_buf[0x10000];
     291             :       fd_set rds;
     292          92 :       int sfd=aStdoutPipe[PIPE_READ];
     293          92 :       int s_ret=0;
     294          92 :       int s_err=0;
     295          92 :       ssize_t o_read=0;
     296          92 :       int o_r_error=0;
     297             :       struct timeval tv;
     298          92 :       tv.tv_sec = ((data_exchange_t *)opaque)->timeout;
     299             :       //tv.tv_sec = TIME_OUT_CHILD;
     300          92 :       tv.tv_usec = 0;
     301          92 :       int timeout=0;
     302          92 :       socket_nonblocking(aStdoutPipe[PIPE_READ]);
     303             :       do
     304             :       {
     305          97 :       FD_ZERO( &rds );
     306          97 :       FD_SET( aStdoutPipe[PIPE_READ], &rds );
     307          97 :       s_ret=select(sfd+1, &rds, NULL, NULL, &tv);
     308          97 :       if (s_ret==SOCKET_ERROR) s_err=errno;
     309          97 :       else if (s_ret>0)
     310             :       {
     311          96 :          o_read=read(aStdoutPipe[PIPE_READ], output_buf, 0x10000);
     312          96 :          if (o_read==0) break;
     313           5 :          else if (o_read >0)
     314             :             //Write_dyn_trace(trace, none,"%.*s",o_read,output_buf);
     315           5 :             Write_dynamic_trace_n (output_buf,o_read,trace);
     316           0 :          else if ((o_r_error=errno)!=EINTR && o_r_error!=EAGAIN) { sock_error_no("std_out pipe read", o_r_error);break; }
     317             :       }
     318             :       else
     319             :       {
     320           1 :          Write_dyn_trace(trace, none,"System Error: timeout executing command!!!");
     321           1 :          exitcode=1;
     322           1 :          timeout=1;
     323           1 :          break;
     324             :       }
     325           5 :       } while (s_ret!=SOCKET_ERROR || ( s_ret==SOCKET_ERROR && (s_err==EINTR || s_err==EAGAIN || s_err==EWOULDBLOCK)));
     326          92 :       close(aStdoutPipe[PIPE_READ]);
     327          92 :       usleep(10000);
     328             :       int status;
     329          92 :       if (timeout)
     330             :       {
     331           1 :          debug("Timeout, kill child\n");
     332           1 :          kill(child_pid,SIGKILL); // TODO need to check with WHOANG
     333             :       }
     334             :       pid_t w=0;
     335             :       int this_error=0;
     336          92 :       while ((w= waitpid(child_pid,&status, 0)) ==-1 && (this_error=errno)==EINTR )
     337             :       {
     338           0 :          debug("exec: interupted Syscall\n");
     339             : 
     340             :       }
     341          92 :       if (w==-1) {
     342           0 :          sock_error_no("waitpid",this_error);
     343           0 :          if (!exitcode) Write_dyn_trace(trace, none,"System error: error on 'waitpid()': %s!!!",strerror(this_error));
     344             :          exitcode=1;
     345             :       }
     346          92 :       else if (WIFEXITED(status))
     347             :       {
     348          90 :          exitcode=WEXITSTATUS(status);
     349          90 :          if (exitcode)
     350             :          {
     351           4 :              debug("exit cmd: %s w error: %d\n",paramlist[0], exitcode);
     352             :          }
     353             :          else
     354             :          {
     355          86 :              debug("[OK]\n");
     356             :              //Write_dyn_trace(trace, green,"\t[OK]\n");
     357             :          }
     358             :       }
     359           2 :       else if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
     360           2 :          alert("command stopped/killed by signal %d : %s\n", WTERMSIG(status),strsignal(WSTOPSIG(status)));
     361           2 :          exitcode=1;
     362           2 :          if (!timeout)
     363           1 :             Write_dyn_trace(trace, red,"\ncommand stopped/killed by signal %d : %s\n", WTERMSIG(status),strsignal(WSTOPSIG(status)));
     364             :       }
     365             :       else
     366             :       {    /* Non-standard case -- may never happen */
     367           0 :       error("Unexpected status child (0x%x)\n", status);exitcode=status;}
     368             :    }
     369             :     else
     370             :     {
     371           0 :         error("Fork failed \n"); Write_dyn_trace(trace, red,"\nsystem Error FORK failed\n");exitcode=1;}
     372          92 :     return (exitcode);
     373             : }
     374             : 
     375             : /*----------------------------------------------------------------------------------
     376             :  * ibash_it(char ** output_buf,int buff_len, const char* valuestring ,cJSON * env_vars,int unpriviledged)
     377             :  * fork and executes a printf of the valuestring
     378             :  * and optional environment a per env_vars
     379             :  * with gid and uid as per data_exchange.gid/uid
     380             :  * with umask according to umask
     381             :  * blocking function with timeout while waiting for feedback and exit-code
     382             :  *
     383             :  * output's the cmd stdout and stderr in output_buff and returns exit code for client
     384             :  *-----------------------------------------------------------------------------------*/
     385           0 : int bash_it(char * output_buf,int buff_len, const char* valuestring ,cJSON * env_vars,int unpriviledged)
     386             : {
     387             : 
     388           0 :    debug("bash_it: start substitute %s\n",valuestring);
     389             :    // feedback buffer
     390           0 :    int v_exit_code=0;
     391           0 :    int * exitcode=&v_exit_code;
     392             : 
     393             :    char shellcommand[1024];
     394           0 :    snprintf(shellcommand,1024,"/usr/bin/printf \"%s\"",valuestring );
     395           0 :    printf("start bash_it\n");
     396             :    pid_t child_pid;
     397             : 
     398             :    int aStdoutPipe[2];
     399           0 :    if (pipe(aStdoutPipe) < 0) {sock_error("allocating pipe for child stdout redirect");snprintf(output_buf,buff_len,"System error\n");return(15);}
     400             :    // Fork_it
     401           0 :    child_pid = fork();
     402             : 
     403           0 :    if(child_pid == 0)
     404             :    {
     405           0 :       fflush(stdout);
     406           0 :       fflush(stderr);
     407             : 
     408             :       // No output to STDOUT/STDERR or our result get's compromissed !!!
     409             :           // NOT even for debug purposes !!!!
     410             :            
     411           0 :       if (dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1) {sock_error("redirecting stdout");exit(2);}
     412             :       // redirect stderr
     413             :       //if (dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1) {sock_error("redirecting stderr");exit(3);}
     414           0 :       int fd2 = open("/dev/null", O_WRONLY);
     415           0 :       if (dup2( fd2 , STDERR_FILENO) == -1) {sock_error("redirecting stderr");exit(3);}
     416           0 :       int fd = open("/dev/null", O_WRONLY);
     417           0 :       if (dup2( fd , STDIN_FILENO) == -1) {sock_error("redirecting stdin");exit(3);}
     418             :       // all these are for use by parent only, so close
     419           0 :       close(aStdoutPipe[PIPE_READ]);
     420           0 :       close(aStdoutPipe[PIPE_WRITE]);
     421             : 
     422             :       //TODO change to user nobody, if no current user
     423             : 
     424           0 :       if (!unpriviledged)
     425             :       {
     426             :         uidguid_t uidguid;
     427           0 :          if (get_uid_gid_user(&uidguid,"nobody")==0)
     428             :          {
     429           0 :          if( setgid(uidguid.gid)) { error("could not set gid\n") ; exit(4);}
     430           0 :          if (setuid(uidguid.uid)) { error("could not set uid\n") ; exit(5);}
     431             :          }
     432             :       }
     433           0 :       if (env_vars && cJSON_GetArraySize(env_vars)>0)
     434             :       {
     435           0 :          cJSON * pos=NULL;
     436           0 :          cJSON_ArrayForEach(pos, env_vars)
     437             :          {
     438           0 :             if (cJSON_IsString(pos))
     439             :             {
     440           0 :                setenv(pos->string,pos->valuestring,1);
     441             :             }
     442             :          }
     443             :       }
     444           0 :            const char * the_shell = getenv("SHELL");
     445           0 :            const char * shname = strrchr(the_shell, '/');
     446           0 :            if (shname)
     447           0 :                    shname++;
     448             :            else
     449             :                    shname = the_shell;
     450             :            
     451           0 :            if (the_shell == NULL || strcmp(shname,"nologin")==0) the_shell = _PATH_BSHELL;
     452             :            
     453           0 :            if (access(the_shell, X_OK) == 0 )
     454             :            {
     455           0 :                    execl(the_shell, shname, "-c", shellcommand, NULL);
     456             :            }
     457             :            else
     458             :            {
     459           0 :                    execlp(shname, "-c", shellcommand, NULL);
     460             :            }
     461             :          // just for safety, should never come here
     462           0 :       _exit(6);
     463             :    }
     464           0 :    else if (child_pid>0) // The Parent
     465             :    {
     466             :       // parent wait's and return result
     467             :       // close unused file descriptors, these are for child only
     468           0 :       close(aStdoutPipe[PIPE_WRITE]);
     469           0 :            usleep(100); // wait a bit to get things settled
     470             :       fd_set rds;
     471           0 :       int sfd=aStdoutPipe[PIPE_READ];
     472           0 :       int s_ret=0;
     473           0 :       int s_err=0;
     474           0 :       size_t o_read=0;
     475           0 :       int o_r_error=0;
     476             :       struct timeval tv;
     477           0 :       tv.tv_sec = 1;
     478             :       //tv.tv_sec = TIME_OUT_CHILD;
     479           0 :       tv.tv_usec = 0;
     480           0 :       int timeout=0;
     481           0 :       memset(output_buf,0,buff_len); // clear buffer!!!
     482           0 :       socket_nonblocking(aStdoutPipe[PIPE_READ]);
     483             :       do
     484             :       {
     485           0 :          FD_ZERO( &rds );
     486           0 :          FD_SET( aStdoutPipe[PIPE_READ], &rds );
     487           0 :          s_ret=select(sfd+1, &rds, NULL, NULL, &tv);
     488           0 :          if (s_ret==SOCKET_ERROR) s_err=errno;
     489           0 :          else if (s_ret>0)
     490             :          {
     491           0 :             size_t len=strlen(output_buf);
     492           0 :             size_t read_now= buff_len-len-1;
     493           0 :             if (read_now<1)
     494             :             {
     495           0 :                error("bash_it truncating output\n");
     496             :                char trunck[1024];
     497           0 :                o_read=read(aStdoutPipe[PIPE_READ], trunck, 1024);
     498             :             }
     499             :             else
     500             :             {
     501           0 :               char * to_buf=output_buf+len;
     502           0 :               o_read=read(aStdoutPipe[PIPE_READ], to_buf, read_now);
     503             :             }
     504           0 :             if (o_read==0) break;
     505           0 :             else if (o_read >0);
     506           0 :             else if ((o_r_error=errno)!=EINTR && o_r_error!=EAGAIN) { sock_error_no("std_out pipe read", o_r_error);break; }
     507             :          }
     508             :          else
     509             :          {
     510           0 :             sprintf((output_buf),"System Error: timeout substituding commands!!!");
     511           0 :             *exitcode=1;
     512           0 :             timeout=1;
     513           0 :             break;
     514             :          }
     515           0 :       } while (s_ret!=SOCKET_ERROR || ( s_ret==SOCKET_ERROR && (s_err==EINTR || s_err==EAGAIN || s_err==EWOULDBLOCK)));
     516           0 :       close(aStdoutPipe[PIPE_READ]);
     517           0 :       usleep(100);
     518             :       int status;
     519           0 :       if (timeout)
     520             :       {
     521           0 :          debug("Timeout, kill child\n");
     522           0 :          kill(child_pid,SIGKILL); // TODO need to check with WHOANG
     523             :       }
     524             :       pid_t w=0;
     525             :       int this_error=0;
     526           0 :       while ((w= waitpid(child_pid,&status, 0)) ==-1 && (this_error=errno)==EINTR )
     527             :       {
     528           0 :          debug("bash_it: interupted Syscall\n");
     529             :       }
     530           0 :       if (w==-1) {
     531           0 :          sock_error_no("waitpid",this_error);
     532           0 :          if (!*exitcode) sprintf((output_buf),"System error: error on 'waitpid()': %s!!!",strerror(this_error));
     533           0 :          *exitcode=1;
     534             :       }
     535           0 :       else if (WIFEXITED(status))
     536             :       {
     537           0 :          *exitcode=WEXITSTATUS(status);
     538           0 :          if (*exitcode)
     539           0 :             info("exit cmd: bash_it w error: %d\n", *exitcode);
     540             :          else
     541             :          {
     542             :             //info("[OK]\n");
     543             :             //sprintf(output_buf+strlen(output_buf),"\t[OK]\n");
     544             :             ;
     545             :          }
     546             :       }
     547           0 :       else if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
     548           0 :          alert("command stopped/killed by signal %d : %s", WTERMSIG(status),strsignal(WSTOPSIG(status)));
     549           0 :          *exitcode=1;
     550           0 :          if (!timeout)
     551           0 :             sprintf((output_buf),"System Error: Bash stopped/killed by signal %d : %s!!!", WTERMSIG(status),strsignal(WSTOPSIG(status)));
     552             :       }
     553             :       else
     554             :       {    /* Non-standard case -- may never happen */
     555           0 :          error("Unexpected status child (0x%x)\n", status);*exitcode=status;}
     556             :    }
     557             :    else
     558             :    {
     559           0 :       error("Fork failed \n"); sprintf((output_buf),"System Error: FORK failed!!!");*exitcode=1;}
     560           0 :    return (*exitcode);
     561             : }
     562             : 
     563             : // New PTY concept
     564             : 
     565           0 : static int done(struct script_control *ctl)
     566             : {
     567             : 
     568           0 :    kill(ctl->child, SIGTERM);        /* make sure we don't create orphans */
     569             : 
     570           0 :    if (WIFSIGNALED(ctl->childstatus))
     571           0 :       return(WTERMSIG(ctl->childstatus));
     572             :    else
     573           0 :       return(WEXITSTATUS(ctl->childstatus));
     574             : }
     575             : 
     576             : 
     577           0 : static void getmaster(struct script_control *ctl)
     578             : {
     579           0 :   ctl->master=-1;
     580             : #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
     581           0 :    int rc=-1;
     582           0 :    rc = openpty(&ctl->master, &ctl->slave, NULL, NULL, NULL);
     583             : 
     584           0 :    if (rc < 0) {
     585           0 :       error("openpty failed");
     586           0 :       kill(0, SIGTERM);
     587           0 :       done(ctl);
     588             :    }
     589             : #else
     590             :    char *pty, *bank, *cp;
     591             : 
     592             :    pty = &ctl->line[strlen("/dev/ptyp")];
     593             :    for (bank = "pqrs"; *bank; bank++) {
     594             :       int ptypos=(int)strlen("/dev/pty");
     595             :       ctl->line[ptypos] = *bank;
     596             :       *pty = '0';
     597             :       if (access(ctl->line, F_OK) != 0)
     598             :          break;
     599             :       for (cp = "0123456789abcdef"; *cp; cp++) {
     600             :          *pty = *cp;
     601             :          ctl->master = open(ctl->line, O_RDWR | O_CLOEXEC);
     602             :          if (ctl->master >= 0) {
     603             :             char *tp = &ctl->line[strlen("/dev/")];
     604             :             int ok;
     605             : 
     606             :             /* verify slave side is usable */
     607             :             *tp = 't';
     608             :             ok = access(ctl->line, R_OK | W_OK) == 0;
     609             :             *tp = 'p';
     610             :             if (ok) return;
     611             :             close(ctl->master);
     612             :             ctl->master = -1;
     613             :          }
     614             :       }
     615             :    }
     616             :    ctl->master = -1;
     617             :    error("out of pty's\n");
     618             : 
     619             :    kill(0, SIGTERM);
     620             :    done(ctl);
     621             : #endif // have libutil
     622           0 : }
     623             : 
     624             : static void getslave(struct script_control *ctl)
     625             : {
     626             : #ifndef HAVE_LIBUTIL
     627             :    ctl->line[strlen("/dev/")] = 't';
     628             :    ctl->slave = open(ctl->line, O_RDWR | O_CLOEXEC);
     629             :    if (ctl->slave < 0) {
     630             :       printf(("cannot open %s"), ctl->line);
     631             :       kill(0, SIGTERM);
     632             :       done(ctl);
     633             :    }
     634             : #endif
     635           0 :    setsid();
     636             :   // ioctl(ctl->slave, TIOCSCTTY, 0);
     637             : }
     638             : 
     639             : /* don't use DBG() stuff here otherwise it will be in  the typescript file */
     640           0 : static void do_shell(struct script_control *ctl)
     641             : {
     642             :    const char *shname;
     643             : 
     644           0 :    getslave(ctl);
     645             : 
     646             :    /* close things irrelevant for this process */
     647           0 :    close(ctl->master);
     648             :    //close(ctl->sigfd);
     649           0 :    int fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
     650           0 :    if (dup2( fd , STDIN_FILENO) == -1) {sock_error("redirecting stdin");exit(3);}
     651             :    //dup2(ctl->slave, STDIN_FILENO);
     652           0 :    dup2(ctl->slave, STDOUT_FILENO);
     653           0 :    dup2(ctl->slave, STDERR_FILENO);
     654           0 :    close(ctl->slave);
     655             : 
     656           0 :    if (ctl->change_ugid && setgid(ctl->gid)) { error("could not set gid\n") ; exit(4);}
     657           0 :    if (ctl->change_ugid && setuid(ctl->uid)) { error("could not set uid\n") ; exit(5);}
     658             : 
     659             :    // user=7 rwx , group=7 r--, other: ---
     660           0 :    umask( S_IRWXO ); /* set newly created file permissions */
     661             : 
     662             : 
     663           0 :    ctl->master = -1;
     664           0 :    shname = strrchr(ctl->shell, '/');
     665           0 :    if (shname)
     666           0 :       shname++;
     667             :    else
     668             :       shname = ctl->shell;
     669             : 
     670             :    // TODO check this!! for now disable
     671             :    //sigprocmask(SIG_SETMASK, &ctl->sigorg, NULL);
     672             : 
     673             :    /*
     674             :     * When invoked from within /etc/csh.login, script spawns a csh shell
     675             :     * that spawns programs that cannot be killed with a SIGTERM. This is
     676             :     * because csh has a documented behavior wherein it disables all
     677             :     * signals when processing the /etc/csh.* files.
     678             :     *
     679             :     * Let's restore the default behavior.
     680             :     */
     681             :    //signal(SIGTERM, SIG_DFL);
     682           0 :     setenv("TERM","xterm-256color",1);
     683           0 :     setenv("CLICOLOR","1",1);
     684             :    /* test extreem env
     685             :     int i;
     686             :     for (i=0;i<150;i++)
     687             :     {
     688             :       char name[20];
     689             :       char value[120];
     690             :       sprintf(name,"CUSTOM_%d",i);
     691             :       sprintf(value,"****************************************************************************%d",i);
     692             :       setenv(name,value,1);
     693             :     }
     694             :     */
     695           0 :    if (ctl->need_envp && ctl->env_vars && cJSON_GetArraySize(ctl->env_vars)>0 )
     696             :    {
     697           0 :       cJSON * pos=NULL;
     698           0 :       cJSON_ArrayForEach(pos, ctl->env_vars)
     699             :       {
     700           0 :          if (cJSON_IsString(pos))
     701             :          {
     702           0 :             setenv(pos->string,pos->valuestring,1);
     703           0 :             debug("env: %s=%s\n",pos->string,pos->valuestring);
     704             :          }
     705           0 :          else debug("skipped non string %s\n",pos->string);
     706             :       }
     707             :       //cJSON_Delete(ctl->env_vars);
     708             :       //ctl->env_vars=NULL;
     709             :    }
     710           0 :    if (access(ctl->shell, X_OK) == 0 )
     711             :    {
     712             :         // printf("child: execl\n");
     713           0 :          execl(ctl->shell, shname, "-c", ctl->shellcommand, NULL);
     714             :    } else
     715             :    {
     716             :          //printf("child: execlp\n");
     717           0 :          execlp(shname, "-c", ctl->shellcommand, NULL);
     718             :    }
     719           0 :    printf(("failed to execute %s\n"), ctl->shell);
     720           0 :    kill(0, SIGTERM);
     721           0 :    exit(done(ctl));
     722             : }
     723             : 
     724           0 : static int do_io(struct script_control *ctl, struct trace_Struct * trace)
     725             : {
     726             :    // parent wait's and return result
     727             :    // close unused file descriptors, these are for child only
     728           0 :    size_t buf_size=0x80000;
     729           0 :    char * output_buf=calloc(1,buf_size);
     730             :    fd_set rds;
     731           0 :    int s_ret=0;
     732           0 :    int s_err=0;
     733           0 :    ssize_t o_read=0;
     734           0 :    int o_r_error=0;
     735             :    struct timeval tv;
     736           0 :    tv.tv_sec = ctl->timeout;
     737           0 :    tv.tv_usec = 0;
     738           0 :    int timeout=0;
     739           0 :    int exitcode=0;
     740           0 :    socket_nonblocking(ctl->master);
     741           0 :    int maxfds=ctl->master;
     742           0 :    socket_nonblocking(ctl->master);
     743             : #if HAVE_LIBUTIL
     744           0 :    if (ctl->close_pipe > ctl->master) maxfds=ctl->close_pipe;
     745           0 :    socket_nonblocking(ctl->close_pipe);
     746             : #endif
     747             :    do
     748             :    {
     749           0 :       FD_ZERO( &rds );
     750           0 :       FD_SET( ctl->master, &rds );
     751             : #if HAVE_LIBUTIL
     752           0 :             FD_SET( ctl->close_pipe, &rds );
     753             : #endif
     754           0 :       tv.tv_sec = ctl->timeout;
     755           0 :       s_ret=select(maxfds+1, &rds, NULL, NULL, &tv);
     756           0 :       if (s_ret==SOCKET_ERROR) s_err=errno;
     757           0 :       else if (s_ret>0)
     758             :       {
     759           0 :         if (FD_ISSET(ctl->master, &rds ))
     760             :         {
     761           0 :            memset(output_buf,0,buf_size);
     762           0 :            o_read=read(ctl->master, output_buf,buf_size-1);
     763           0 :            if (o_read==0) {debug("trace: EOF\n");;break;}
     764           0 :            else if (o_read >0){
     765           0 :               debug ("trace: Received %d bytes",(int)o_read);
     766           0 :               Write_dynamic_trace_n (output_buf,o_read,trace);
     767             :             }
     768           0 :            else if ((o_r_error=errno)!=EINTR && o_r_error!=EAGAIN) { sock_error_no("pty read", o_r_error);break; }
     769             :          }
     770             : #if HAVE_LIBUTIL
     771           0 :          if (FD_ISSET(ctl->close_pipe, &rds ))
     772             :          {
     773           0 :             memset(output_buf,0,buf_size);
     774           0 :             ssize_t csr=read(ctl->close_pipe,output_buf,buf_size-1);
     775           0 :             if (csr==0) {debug ("pipeclosed\n");break;}
     776           0 :             else if (csr<0 && (o_r_error=errno)!=EINTR && o_r_error!=EAGAIN) { sock_error_no("close pipe read", o_r_error);break; }
     777             :             else break;
     778             :          }
     779             : #endif
     780             :       }
     781             :       else
     782             :       {
     783           0 :         Write_dyn_trace(trace, red,"System Error: timeout executing command!!!\n");
     784           0 :          timeout=1;
     785           0 :          break;
     786             :       }
     787           0 :    } while (s_ret!=SOCKET_ERROR || ( s_ret==SOCKET_ERROR && (s_err==EINTR || s_err==EAGAIN || s_err==EWOULDBLOCK)));
     788             :    //printf("outputbuf p %p\n",output_buf);
     789           0 :    usleep(10000);
     790             :    int status;
     791           0 :    if (output_buf) free(output_buf);
     792           0 :    output_buf=NULL;
     793           0 :    if (timeout)
     794             :    {
     795           0 :       debug("Timeout, kill child\n");
     796           0 :       kill(ctl->child,SIGKILL); // TODO need to check with WHOANG
     797             :    }
     798             :    pid_t w=0;
     799             :    int this_error=0;
     800           0 :    while ((w= waitpid(ctl->child,&status, 0)) ==-1 && (this_error=errno)==EINTR )
     801             :    {
     802           0 :       debug("exec: interupted Syscall\n");
     803             :    }
     804           0 :    if (w==-1) {
     805           0 :       sock_error_no("waitpid",this_error);
     806             :       //if (!exitcode)
     807           0 :       Write_dyn_trace(trace, red,"System error: error on 'waitpid()': %s!!!\n",strerror(this_error));
     808           0 :       exitcode=1;
     809             :    }
     810           0 :    else if (WIFEXITED(status))
     811             :    {
     812           0 :       exitcode=WEXITSTATUS(status);
     813           0 :       if (exitcode)
     814             :       {
     815           0 :          debug("exit cmd: %s w error: %d\n",ctl->shellcommand, exitcode);
     816             :       }
     817             :       else
     818             :       {
     819             :          //debug("[OK]\n");
     820             :          //Write_dyn_trace(trace, green,"\t[OK]\n");
     821             :       }
     822             :    }
     823           0 :    else if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
     824           0 :       alert("command stopped/killed by signal %d : %s\n", WTERMSIG(status),strsignal(WSTOPSIG(status)));
     825           0 :       exitcode=1;
     826           0 :       if (!timeout)
     827           0 :          Write_dyn_trace(trace, red,"\ncommand stopped/killed by signal %d : %s\n", WTERMSIG(status),strsignal(WSTOPSIG(status)));
     828             :    }
     829             :    else
     830             :    {    /* Non-standard case -- may never happen */
     831           0 :       error("Unexpected status child (0x%x)\n", status);
     832           0 :       exitcode=status;
     833             :    }
     834             : 
     835             :    //restore_tty(ctl, TCSADRAIN);
     836           0 :    return exitcode;
     837             : }
     838             : 
     839           0 : int exec_color(void * opaque)
     840             : {
     841           0 :    debug("start command color exec\n");
     842             : 
     843           0 :    data_exchange_t * data_exchange=((data_exchange_t *)opaque);
     844             :    // feedback buffer
     845           0 :    int exit_code=0;
     846           0 :    struct trace_Struct *trace=((data_exchange_t *)opaque)->trace;
     847             : 
     848             : 
     849             :    char line[20];
     850           0 :    sprintf(line ,"/dev/ptyXX");
     851           0 :    struct script_control ctl =
     852             :    {
     853           0 :       .need_envp=data_exchange->needenvp,
     854           0 :       .env_vars =data_exchange->env_json,
     855             : #if !HAVE_LIBUTIL
     856             :             .line = line,
     857             : #else
     858             :             .close_pipe= -1,
     859             : #endif
     860             :       .master = -1,
     861           0 :       .gid=data_exchange->gid,
     862           0 :       .uid=data_exchange->uid,
     863           0 :       .timeout=((data_exchange_t *)opaque)->timeout,
     864             :       0
     865             :    };
     866             : 
     867             : 
     868           0 :    const char* shname=NULL;
     869           0 :    ctl.shell = getenv("SHELL");
     870           0 :    shname = strrchr(ctl.shell, '/');
     871           0 :    if (shname)
     872           0 :       shname++;
     873             :    else
     874             :       shname = ctl.shell;
     875           0 :    if (ctl.shell == NULL || strcmp(shname,"nologin")==0)
     876           0 :       ctl.shell = _PATH_BSHELL;
     877             : 
     878           0 :    getmaster(&ctl);
     879             : 
     880           0 :    if (ctl.master==INVALID_SOCKET)
     881             :    {
     882           0 :     Write_dyn_trace(trace, red,"\nsystem Error no PTY\n");
     883           0 :     error("system Error no PTY\n");
     884           0 :      return (-1);
     885             :    }
     886           0 :    ctl.shellcommand=data_exchange->shellcommand;
     887             :     //strdup("echo -e \"\033[31;1m print it RED \033[0;m \" && sleep 1 && echo -e \"\033[32;1m print it GREEN \033[0;m \" && env && ls -la&& exit 3");
     888             : #if HAVE_LIBUTIL
     889             :         int aStdoutPipe[2];
     890           0 :         if (pipe(aStdoutPipe) < 0) {sock_error("allocating pipe for child stdout redirect");Write_dyn_trace(trace, red,"System error allocating pipe\n");return(15);}
     891             : #endif
     892           0 :    fflush(stdout);
     893           0 :    debug("forking\n");
     894           0 :    ctl.child = fork();
     895           0 :    switch (ctl.child) {
     896             :       case -1: /* error */
     897             : #if HAVE_LIBUTIL
     898           0 :         close (aStdoutPipe[0]);
     899           0 :         close (aStdoutPipe[1]);
     900             : #endif
     901           0 :         error("Fork failed \n");
     902           0 :          Write_dyn_trace(trace, red,"\nsystem Error FORK failed\n");
     903           0 :          kill(0, SIGTERM);
     904           0 :          done(&ctl);
     905           0 :          exit_code=EXIT_FAILURE;
     906           0 :          break;
     907             :       case 0: /* child */
     908             : #if HAVE_LIBUTIL
     909           0 :          close(aStdoutPipe[PIPE_READ]);
     910           0 :          socket_O_CLOEXEC(aStdoutPipe[PIPE_WRITE]);
     911             : #endif
     912           0 :          printf("is it flushed?\n");
     913           0 :          fflush(stdout);
     914           0 :          fflush(stderr);
     915           0 :          do_shell(&ctl);
     916             :          break;
     917             :       default: /* parent */
     918             : #if HAVE_LIBUTIL
     919           0 :          close(aStdoutPipe[PIPE_WRITE]);
     920           0 :          socket_nonblocking(aStdoutPipe[PIPE_READ]);
     921           0 :          ctl.close_pipe=aStdoutPipe[PIPE_READ];
     922             : #endif
     923           0 :          exit_code=do_io(&ctl,trace);
     924           0 :          break;
     925             :    }
     926           0 :    close(ctl.master);
     927           0 :    return exit_code;
     928             : }

Generated by: LCOV version 1.10