LCOV - code coverage report
Current view: top level - src - wordexp_var.c (source / functions) Hit Total Coverage
Test: oc-runner-0.0.51.5.29c69 Code Coverage Lines: 63 214 29.4 %
Date: 2018-10-15 12:44:38 Functions: 2 4 50.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  Copyright © 2018 by Danny Goossen, Gioxa Ltd. All rights reserved.
       3             : 
       4             :  This file is part of the oc-runner
       5             : 
       6             :  MIT License
       7             : 
       8             :  Permission is hereby granted, free of charge, to any person obtaining a copy
       9             :  of this software and associated documentation files (the "Software"), to deal
      10             :  in the Software without restriction, including without limitation the rights
      11             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      12             :  copies of the Software, and to permit persons to whom the Software is
      13             :  furnished to do so, subject to the following conditions:
      14             : 
      15             :  The above copyright notice and this permission notice shall be included in all
      16             :  copies or substantial portions of the Software.
      17             : 
      18             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      19             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      20             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      21             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      22             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      23             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      24             :  SOFTWARE.
      25             : 
      26             :  */
      27             : /*! \file wordexp_var.c
      28             :  *  \briefexpand safely variables and commandlines
      29             :  *  \author Created by Danny Goossen on 23/11/17.
      30             :  *  \copyright 2018, Danny Goossen. MIT License
      31             :  *
      32             :  */
      33             : 
      34             : #include "deployd.h"
      35             : #include "wordexp_var.h"
      36             : 
      37             : 
      38             : /**
      39             :  * \brief helper function to make socket non blocking
      40             :  * \param sock the socket
      41             :  * \return -1 on failure,
      42             :  * \note on failure, caller is responsible to close socket
      43             :  */
      44             : static int socket_nonblocking (int sock);
      45             : 
      46          13 : int word_exp_var(char * output_buf,int buff_len, const char* valuestring ,const cJSON * env_vars)
      47             : {
      48             : 
      49             :         //alert("word_exp: start substitute %s\n",valuestring);
      50             :         // feedback buffer
      51          13 :         int v_exit_code=0;
      52          13 :         int * exitcode=&v_exit_code;
      53             : 
      54             :         pid_t child_pid;
      55             : 
      56             :         int aStdoutPipe[2];
      57          13 :         if (pipe(aStdoutPipe) < 0) {sock_error("allocating pipe for child stdout redirect");snprintf(output_buf,buff_len,"System error\n");return(15);}
      58             :         // Fork_it
      59          13 :         child_pid = fork();
      60             : 
      61          26 :         if(child_pid == 0)
      62             :         {
      63             :                 //close uneeded read pipeend
      64          13 :                 close(aStdoutPipe[PIPE_READ]);
      65             : 
      66             :                 // set environment
      67          13 :                 if (env_vars && cJSON_GetArraySize(env_vars)>0)
      68             :                 {
      69          13 :                         cJSON * pos=NULL;
      70          46 :                         cJSON_ArrayForEach(pos, env_vars)
      71             :                         {
      72          33 :                                 if (cJSON_IsString(pos))
      73             :                                 {
      74          33 :                                         setenv(pos->string,pos->valuestring,1);
      75             :                                         //fprintf(stderr,"add env var: %s\n",pos->valuestring);
      76             :                                 }
      77             :                         }
      78             :                 }
      79             :                 // expand it
      80             :                 wordexp_t word_re;
      81             : 
      82             : 
      83             :                         // errors on undef and commands
      84          13 :                         int res=0;
      85          13 :                         switch (res=wordexp(  valuestring, &word_re, WRDE_NOCMD|WRDE_SHOWERR))
      86             :                         {
      87             :                                 case WRDE_NOSPACE:
      88             :                                         /* If the error was WRDE_NOSPACE,
      89             :                                          then perhaps part of the result was allocated. */
      90           0 :                                         wordfree (&word_re);
      91           0 :                                         break;
      92             :                                 default: /* Some other error. */
      93             :                                         break;
      94             :                         }
      95          13 :                         if (res==0)
      96             :                         {
      97             :                                 // write back the result
      98             :                                 int i=0;
      99           9 :                                 for (i=0;i<word_re.we_wordc;i++)
     100             :                                 {
     101           9 :                                         write( aStdoutPipe[PIPE_WRITE],word_re.we_wordv[i],strlen(word_re.we_wordv[i]));
     102           9 :                                         if (i+1!=word_re.we_wordc) write( aStdoutPipe[PIPE_WRITE]," ",1);
     103             :                                 }
     104             :                                 //cleanup
     105          11 :                                 wordfree(&word_re);
     106             :                         }
     107             :                 //signal linux end, linux does not get signal on close
     108          13 :                 shutdown(aStdoutPipe[PIPE_WRITE], SHUT_WR);
     109          13 :                 close(aStdoutPipe[PIPE_WRITE]);
     110             :                 //exit, job done succesfull
     111          13 :                 exit(res);
     112             :         }
     113          13 :         else if (child_pid>0) // The Parent
     114             :         {
     115             :                 // parent wait's and return result
     116             :                 // close unused file descriptors, these are for child only
     117          13 :                 close(aStdoutPipe[PIPE_WRITE]);
     118          13 :                 usleep(100); // wait a bit to get things settled
     119             :                 fd_set rds;
     120          13 :                 int sfd=aStdoutPipe[PIPE_READ];
     121          13 :                 int s_ret=0;
     122          13 :                 int s_err=0;
     123          13 :                 size_t o_read=0;
     124          13 :                 int o_r_error=0;
     125             :                 struct timeval tv;
     126          13 :                 tv.tv_sec = 10;
     127          13 :                 tv.tv_usec = 0;
     128          13 :                 int timeout=0;
     129          13 :                 memset(output_buf,0,buff_len); // clear buffer!!!
     130          13 :                 socket_nonblocking(aStdoutPipe[PIPE_READ]);
     131             :                 do
     132             :                 {
     133          22 :                         FD_ZERO( &rds );
     134          22 :                         FD_SET( aStdoutPipe[PIPE_READ], &rds );
     135          22 :                         tv.tv_sec = 10;
     136          22 :                         s_ret=select(sfd+1, &rds, NULL, NULL, &tv);
     137          22 :                         if (s_ret==SOCKET_ERROR) s_err=errno;
     138          22 :                         else if (s_ret>0)
     139             :                         {
     140          22 :                                 size_t len=strlen(output_buf);
     141          22 :                                 size_t read_now= buff_len-len-1;
     142          22 :                                 if (read_now<1)
     143             :                                 {
     144           0 :                                         error("bash_it truncating output\n");
     145             :                                         char trunck[1024];
     146           0 :                                         o_read=read(aStdoutPipe[PIPE_READ], trunck, 1024);
     147             :                                 }
     148             :                                 else
     149             :                                 {
     150          22 :                                         char * to_buf=output_buf+len;
     151          22 :                                         o_read=read(aStdoutPipe[PIPE_READ], to_buf, read_now);
     152             :                                 }
     153          22 :                                 if (o_read==0) break;
     154           9 :                                 else if (o_read >0);
     155           0 :                                 else if ((o_r_error=errno)!=EINTR && o_r_error!=EAGAIN) { sock_error_no("std_out pipe read", o_r_error);break; }
     156             :                         }
     157             :                         else
     158             :                         {
     159           0 :                                 sprintf((output_buf),"System Error: timeout substituding commands!!!");
     160           0 :                                 *exitcode=1;
     161           0 :                                 timeout=1;
     162           0 :                                 break;
     163             :                         }
     164           9 :                 } while (s_ret!=SOCKET_ERROR || ( s_ret==SOCKET_ERROR && (s_err==EINTR || s_err==EAGAIN || s_err==EWOULDBLOCK)));
     165          13 :                 close(aStdoutPipe[PIPE_READ]);
     166          13 :                 usleep(100);
     167             :                 int status;
     168          13 :                 if (timeout)
     169             :                 {
     170           0 :                         debug("Timeout, kill child\n");
     171           0 :                         kill(child_pid,SIGKILL); // TODO need to check with WHOANG
     172             :                 }
     173             :                 pid_t w=0;
     174             :                 int this_error=0;
     175          13 :                 while ((w= waitpid(child_pid,&status, 0)) ==-1 && (this_error=errno)==EINTR )
     176             :                 {
     177           0 :                         debug("bash_it: interupted Syscall\n");
     178             :                 }
     179          13 :                 if (w==-1) {
     180           0 :                         sock_error_no("waitpid",this_error);
     181           0 :                         if (!*exitcode) sprintf((output_buf),"System error: error on 'waitpid()': %s!!!",strerror(this_error));
     182           0 :                         *exitcode=1;
     183             :                 }
     184          13 :                 else if (WIFEXITED(status))
     185             :                 {
     186          13 :                         *exitcode=WEXITSTATUS(status);
     187          13 :                         if (*exitcode)
     188           2 :                                 info("exit cmd: bash_it w error: %d\n", *exitcode);
     189             :                         else
     190             :                         {
     191             :                                 //info("[OK]\n");
     192             :                                 //sprintf(output_buf+strlen(output_buf),"\t[OK]\n");
     193             :                                 ;
     194             :                         }
     195             :                 }
     196           0 :                 else if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
     197           0 :                         alert("command stopped/killed by signal %d : %s", WTERMSIG(status),strsignal(WSTOPSIG(status)));
     198           0 :                         *exitcode=1;
     199           0 :                         if (!timeout)
     200           0 :                                 sprintf((output_buf),"System Error: Bash stopped/killed by signal %d : %s!!!", WTERMSIG(status),strsignal(WSTOPSIG(status)));
     201             :                 }
     202             :                 else
     203             :                 {    /* Non-standard case -- may never happen */
     204           0 :                         error("Unexpected status child (0x%x)\n", status);*exitcode=status;}
     205             :         }
     206             :         else
     207             :         {
     208           0 :                 error("Fork failed \n"); sprintf((output_buf),"System Error: FORK failed!!!");*exitcode=1;}
     209          13 :         return (*exitcode);
     210             : }
     211             : 
     212             : 
     213             : 
     214           0 : void word_exp_var_free(word_exp_var_t * wev)
     215             : {
     216           0 :         if (wev->argv) free((char**)wev->argv);
     217           0 :         wev->argv=NULL;
     218           0 :         if(wev->args_obj) cJSON_Delete(wev->args_obj);
     219           0 :         wev->args_obj=NULL;
     220           0 :         if(wev->errmsg) free(wev->errmsg);
     221           0 :         wev->errmsg=NULL;
     222           0 :         wev->argc=0;
     223           0 : }
     224             : 
     225             : 
     226           0 : int word_exp_var_dyn(word_exp_var_t * wev, const char* valuestring ,const cJSON * env_vars,const char * dir)
     227             : {
     228             : 
     229             :         //alert("word_exp: start substitute %s\n",valuestring);
     230             :         // feedback buffer
     231           0 :         int v_exit_code=0;
     232           0 :         int * exitcode=&v_exit_code;
     233             : 
     234             :         pid_t child_pid;
     235             : 
     236             :         int aStdoutPipe[2];
     237           0 :         if (pipe(aStdoutPipe) < 0)
     238             :         {
     239           0 :                 sock_error("allocating pipe for child stdout redirect");
     240             : 
     241           0 :                 asprintf(&wev->errmsg,"System error\n");
     242           0 :                 return(15);
     243             :         }
     244             :         // Fork_it
     245           0 :         child_pid = fork();
     246             : 
     247           0 :         if(child_pid == 0)
     248             :         {
     249             :                 //close uneeded read pipeend
     250           0 :                 close(aStdoutPipe[PIPE_READ]);
     251           0 :                 dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO);
     252             :                 // set environment
     253           0 :                 if (env_vars && cJSON_GetArraySize(env_vars)>0)
     254             :                 {
     255           0 :                         cJSON * pos=NULL;
     256           0 :                         cJSON_ArrayForEach(pos, env_vars)
     257             :                         {
     258           0 :                                 if (cJSON_IsString(pos))
     259             :                                 {
     260           0 :                                         setenv(pos->string,pos->valuestring,1);
     261             :                                 }
     262             :                         }
     263             :                 }
     264             :                 //const char * project_dir=cJSON_get_key(env_vars, "CI_PROJECT_DIR");
     265             :                 //int samedir=0;
     266             :                 //if (dir && project_dir && strcmp(project_dir,dir)==0) samedir=1;
     267             :                 // expand it
     268             :                 wordexp_t word_re;
     269             :                 // TODO need safe_chdir, stat first!!!
     270           0 :                 if (dir && strlen(dir)>0) chdir(dir);
     271             : 
     272             :                 // errors on undef and commands
     273           0 :                 int res=0;
     274           0 :                 switch (res=wordexp(  valuestring, &word_re, WRDE_NOCMD))
     275             :                 {
     276             :                         case 0:
     277             :                         {
     278             :                                 // write back the result
     279           0 :                                 cJSON * cjsonStringArray=cJSON_CreateStringArray((const char**)word_re.we_wordv,(int) word_re.we_wordc);
     280           0 :                                 if (cjsonStringArray)
     281             :                                 {
     282             :                                         /*
     283             :                                         cJSON * item_s=NULL;
     284             :                                     // loop results and prepend path if needed
     285             : 
     286             :                                         cJSON_ArrayForEach(item_s, cjsonStringArray)
     287             :                                         {
     288             :                                                 if (!(strchr(item_s->valuestring,'/')==word_re.we_wordv[i] || samedir || !project_dir))
     289             :                                                 {
     290             :                                                         char *tmp=item_s->valuestring;
     291             :                                                         asprintf(&item_s->valuestring, "%s/%s",project_dir,tmp);
     292             :                                                         free(tmp);
     293             :                                                 }
     294             :                                         }
     295             :                                          */
     296             :                                         // serialize
     297           0 :                                         char * result=cJSON_Print(cjsonStringArray);
     298           0 :                                         size_t size_total=strlen(result);
     299           0 :                                         size_t size_total_writen=0;
     300           0 :                                         size_t size_towrite=0;
     301           0 :                                         ssize_t size_writen=1;
     302           0 :                                         while (size_total_writen<size_total && size_writen>0)
     303             :                                         {
     304           0 :                                                 size_towrite=size_total-size_total_writen;
     305           0 :                                                 size_writen=write( aStdoutPipe[PIPE_WRITE],&result[size_total_writen],size_towrite);
     306           0 :                                                 if (size_writen>=0) size_total_writen+=size_writen;
     307             :                                         }
     308           0 :                                         if (size_writen<0) res=-1;
     309           0 :                                         cJSON_Delete(cjsonStringArray);
     310           0 :                                         cjsonStringArray=NULL;
     311             : 
     312           0 :                                         free(result);
     313           0 :                                         result=NULL;
     314             :                                 }
     315           0 :                                 else fprintf(stderr, "No string array\n");
     316             :                                 //cleanup
     317           0 :                                 wordfree(&word_re);
     318             : 
     319             :                         }
     320           0 :                                 break;
     321             :                         case WRDE_BADVAL:
     322           0 :                                 fprintf(stderr,"An undefined shell variable was referenced.\n");
     323           0 :                                 break;
     324             :                         case WRDE_BADCHAR:
     325           0 :                                 fprintf(stderr,"Illegal occurrence of newline or one of |, &, ;, <, >, (, ),{, }.\n");
     326           0 :                                 break;
     327             :                         case WRDE_SYNTAX:
     328           0 :                                 fprintf(stderr,"Syntax error, such as unbalanced parentheses or unmatched quotes.\n");
     329           0 :                                 break;
     330             :                         case WRDE_CMDSUB:
     331           0 :                                 fprintf(stderr,"Command substitution like `cmd` and $(cmd) are not allowed.\n");
     332           0 :                                 break;
     333             :                         case WRDE_NOSPACE:
     334             :                                 /* If the error was WRDE_NOSPACE,
     335             :                                  then perhaps part of the result was allocated. */
     336           0 :                                 fprintf(stderr,"Out of Memory.\n");
     337           0 :                                 wordfree (&word_re);
     338           0 :                                 break;
     339             :                         default: /* Some other error. */
     340             :                                 break;
     341             :                 }
     342             :                 //signal linux end, linux does not get signal on close
     343           0 :                 shutdown(aStdoutPipe[PIPE_WRITE], SHUT_WR);
     344           0 :                 close(aStdoutPipe[PIPE_WRITE]);
     345             :                 //exit, job done succesfull
     346           0 :                 exit(res);
     347             :         }
     348           0 :         else if (child_pid>0) // The Parent
     349             :         {
     350             :                 // parent wait's and return result
     351             :                 // close unused file descriptors, these are for child only
     352           0 :                 close(aStdoutPipe[PIPE_WRITE]);
     353           0 :                 usleep(100); // wait a bit to get things settled
     354             :                 fd_set rds;
     355           0 :                 int sfd=aStdoutPipe[PIPE_READ];
     356           0 :                 int s_ret=0;
     357           0 :                 int s_err=0;
     358           0 :                 size_t o_read=0;
     359           0 :                 int o_r_error=0;
     360             :                 struct timeval tv;
     361           0 :                 tv.tv_sec = 10;
     362           0 :                 tv.tv_usec = 0;
     363           0 :                 int timeout=0;
     364           0 :                 dynbuffer * dyn_buff=dynbuffer_init();
     365           0 :                 socket_nonblocking(aStdoutPipe[PIPE_READ]);
     366             :                 do
     367             :                 {
     368             :                         char * read_buffer[1024];
     369           0 :                         memset(read_buffer, 0, 1024);
     370           0 :                         FD_ZERO( &rds );
     371           0 :                         FD_SET( aStdoutPipe[PIPE_READ], &rds );
     372           0 :                         tv.tv_sec = 10;
     373           0 :                         s_ret=select(sfd+1, &rds, NULL, NULL, &tv);
     374           0 :                         if (s_ret==SOCKET_ERROR) s_err=errno;
     375           0 :                         else if (s_ret>0)
     376             :                         {
     377             : 
     378             : 
     379           0 :                                 o_read=read(aStdoutPipe[PIPE_READ], read_buffer,1024);
     380           0 :                                 if (o_read==0) break;
     381           0 :                                 else if (o_read >0) dynbuffer_write_n(read_buffer, o_read, dyn_buff);
     382           0 :                                 else if ((o_r_error=errno)!=EINTR && o_r_error!=EAGAIN) { sock_error_no("std_out pipe read", o_r_error);break; }
     383             :                         }
     384             :                         else
     385             :                         {
     386           0 :                                 dynbuffer_write_v(dyn_buff,"System Error: timeout substituding commands!!!");
     387           0 :                                 *exitcode=1;
     388           0 :                                 timeout=1;
     389           0 :                                 break;
     390             :                         }
     391           0 :                 } while (s_ret!=SOCKET_ERROR || ( s_ret==SOCKET_ERROR && (s_err==EINTR || s_err==EAGAIN || s_err==EWOULDBLOCK)));
     392           0 :                 close(aStdoutPipe[PIPE_READ]);
     393           0 :                 usleep(100);
     394             :                 int status;
     395           0 :                 if (timeout)
     396             :                 {
     397           0 :                         debug("Timeout, kill child\n");
     398           0 :                         kill(child_pid,SIGKILL); // TODO need to check with WHOANG
     399             :                 }
     400             :                 pid_t w=0;
     401             :                 int this_error=0;
     402           0 :                 while ((w= waitpid(child_pid,&status, 0)) ==-1 && (this_error=errno)==EINTR )
     403             :                 {
     404           0 :                         debug("bash_it: interupted Syscall\n");
     405             :                 }
     406           0 :                 if (w==-1) {
     407           0 :                         sock_error_no("waitpid",this_error);
     408           0 :                         if (!*exitcode) dynbuffer_write_v(dyn_buff,"System error: error on 'waitpid()': %s!!!",strerror(this_error));
     409           0 :                         *exitcode=1;
     410             :                 }
     411           0 :                 else if (WIFEXITED(status))
     412             :                 {
     413           0 :                         *exitcode=WEXITSTATUS(status);
     414           0 :                         if (*exitcode)
     415           0 :                                 info("exit cmd: wordexp w error: %d\n", *exitcode);
     416             :                         else
     417             :                         {
     418             :                                 //info("[OK]\n");
     419             :                                 //sprintf(output_buf+strlen(output_buf),"\t[OK]\n");
     420             :                                 ;
     421             :                         }
     422             :                 }
     423           0 :                 else if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
     424           0 :                         alert("command stopped/killed by signal %d : %s", WTERMSIG(status),strsignal(WSTOPSIG(status)));
     425           0 :                         *exitcode=1;
     426           0 :                         if (!timeout)
     427           0 :                                 dynbuffer_write_v(dyn_buff,"System Error: wordexp stopped/killed by signal %d : %s!!!", WTERMSIG(status),strsignal(WSTOPSIG(status)));
     428             :                 }
     429             :                 else
     430             :                 {    /* Non-standard case -- may never happen */
     431           0 :                         error("Unexpected status child (0x%x)\n", status);*exitcode=status;
     432             :                 }
     433           0 :                 if (*exitcode)
     434           0 :                         dynbuffer_pop(&dyn_buff, &wev->errmsg, NULL);
     435             :                 else
     436             :                 {
     437           0 :                         char * tmp=NULL;
     438           0 :                         dynbuffer_pop(&dyn_buff, &tmp, NULL);
     439           0 :                         wev->args_obj=cJSON_Parse(tmp);
     440           0 :                         if (wev->args_obj)
     441             :                         {
     442           0 :                                 wev->argc=cJSON_GetArraySize(wev->args_obj);
     443           0 :                                 wev->argv=calloc(wev->argc+1,sizeof(char*const)); // end with null pointer, just in case we need!
     444           0 :                                 char**walk=(char**)wev->argv;
     445           0 :                                 cJSON*item=NULL;
     446           0 :                                 cJSON_ArrayForEach (item,wev->args_obj)
     447             :                                 {
     448           0 :                                         *walk=item->valuestring;
     449           0 :                                         walk++;
     450             :                                 }
     451             :                         }
     452             :                 }
     453           0 :                 dynbuffer_clear(&dyn_buff);
     454             :         }
     455             :         else
     456             :         {
     457           0 :                 error("Fork failed \n");
     458           0 :                 asprintf(&wev->errmsg,"System Error: FORK failed!!!");
     459           0 :                 *exitcode=1;
     460             :         }
     461             : 
     462           0 :         return (*exitcode);
     463             : }
     464             : 
     465             : 
     466          13 : static int socket_nonblocking (int sock)
     467             : {
     468             :         int flags;
     469             : 
     470          13 :         if(sock != INVALID_SOCKET)
     471             :         {
     472          13 :                 flags = fcntl (sock, F_GETFL, 0);
     473          13 :                 return fcntl (sock, F_SETFL, flags | O_NONBLOCK | O_CLOEXEC | SO_KEEPALIVE);//| O_NDELAY);
     474             :         }
     475             :         return -1;
     476             : 
     477             : }

Generated by: LCOV version 1.13