xdasd_exec.c

Go to the documentation of this file.
00001 /*----------------------------------------------------------------------------
00002  * Copyright (c) 2006, Novell, Inc.
00003  * All rights reserved.
00004  * 
00005  * Redistribution and use in source and binary forms, with or without 
00006  * modification, are permitted provided that the following conditions are 
00007  * met:
00008  * 
00009  *     * Redistributions of source code must retain the above copyright 
00010  *       notice, this list of conditions and the following disclaimer.
00011  *     * Redistributions in binary form must reproduce the above copyright 
00012  *       notice, this list of conditions and the following disclaimer in the 
00013  *       documentation and/or other materials provided with the distribution.
00014  *     * Neither the name of the Novell nor the names of its contributors 
00015  *       may be used to endorse or promote products derived from this 
00016  *       software without specific prior written permission.
00017  * 
00018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
00019  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
00020  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
00021  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
00022  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
00023  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
00024  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
00025  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
00026  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
00027  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *--------------------------------------------------------------------------*/
00030 
00039 #include "xdasd_exec.h"
00040 #include "xdasd_log.h"
00041 
00042 #include <stdio.h>
00043 #include <stdlib.h>
00044 #include <string.h>
00045 #include <errno.h>
00046 
00047 #ifdef _WIN32
00048 # define WIN32_LEAN_AND_MEAN
00049 # include <windows.h>
00050 # include <process.h>
00051 # include <direct.h>
00052 #else
00053 # include <unistd.h>
00054 # include <sys/types.h>
00055 # include <sys/wait.h>
00056 #endif
00057 
00058 #ifdef XDASD_EXEC_TEST
00059 # define INITIAL_BUFSZ_MULTIPLIER   1
00060 #else
00061 # define INITIAL_BUFSZ_MULTIPLIER   2
00062 #endif
00063 
00064 #define XDAS_FIELD_COUNT 33
00065 
00076 static char * ex_substitute_vars(const char * script, const char ** msgflds)
00077 {
00078    char * p, * text;
00079    size_t scriptsz = strlen(script) + 1;
00080    size_t allocsz = scriptsz * INITIAL_BUFSZ_MULTIPLIER;
00081 
00082    if ((text = (char *)malloc(allocsz)) == 0)
00083    {
00084       xdasd_log(0, "exec: Memory allocation failure.\n");
00085       return 0;
00086    }
00087    memcpy(text, script, scriptsz);
00088 
00089    p = text;
00090    while ((p = strstr(p, "$XDAS")) != 0)
00091    {
00092       int fldnum = 0;
00093       size_t flen;                     /* length of field value */
00094       size_t vlen = 5;                 /* length of var (5 in "$XDAS") */
00095       const char * msg = msgflds[0];   /* assume no trailing field number */
00096       const char * limit = msgflds[XDAS_FIELD_COUNT] - 1;
00097 
00098       /* locate field if field value specified */
00099       if (p[5] == '(')
00100       {
00101          while (p[vlen++] != ')') /* empty */ ; /* count to closing paren */
00102          fldnum = strtoul(p + 6, 0, 0);
00103          /* assert(fldnum >= 0 && fldnum <= 32); */
00104          msg = msgflds[fldnum];
00105          limit = msgflds[fldnum + 1] - 1;
00106       }
00107 
00108       /* grow buffer if necessary - if new scriptsz grows beyond allocsz */
00109       flen = limit - msg;
00110       if (scriptsz + flen - vlen > allocsz)
00111       {
00112          char * newtext;
00113          while (scriptsz + flen - vlen > allocsz)
00114             allocsz *= 2;
00115          if ((newtext = (char *)realloc(text, allocsz)) == 0)
00116          {
00117             xdasd_log(0, "exec: Memory allocation failure.\n");
00118             free(text);
00119             return 0;
00120          }
00121          p = newtext + (p - text);  /* repoint into new buffer */
00122          text = newtext;
00123       }
00124 
00125       /* replace variable text with message field text */
00126       memmove(p + flen, p + vlen, scriptsz - (p - text + vlen));
00127       memcpy(p, msg, flen);
00128       scriptsz += flen - vlen;   /* Note that flen - vlen may be negative */
00129 
00130       /* move p past inserted text */
00131       p += flen;
00132    }
00133    return text;
00134 }
00135 
00151 static int ex_exec_script(const char * script)
00152 {
00153 #ifdef _WIN32
00154 
00155 # define SHELL_ENVAR    "COMSPEC"
00156 # define DEFAULT_SHELL  "CMD.EXE"
00157 
00158 # if 0   /* batch file mode disabled */
00159 
00160    /* This is the batch file method - this method would be needed on MSDOS
00161     * systems, and Win95/98, but we don't care about them anymore.
00162     */
00163    static char tmpath[MAX_PATH - 18] = "";
00164 
00165    char cmdline[MAX_PATH + 3];
00166    PROCESS_INFORMATION pi;
00167    STARTUPINFO sui;
00168    DWORD pecode = 0;
00169    FILE * fp;
00170    char * shell;
00171 
00172    if ((shell = getenv(SHELL_ENVAR)) == 0)
00173       shell = DEFAULT_SHELL;
00174 
00175    if (*tmpath == 0 && GetTempPath(sizeof(tmpath), tmpath) == 0)
00176    {
00177       xdasd_log(0, "exec: Error getting TEMP directory, %ul.\n", GetLastError());
00178       return -1;
00179    }
00180    strcpy(cmdline, "/c ");
00181    if (GetTempFileName(tmpath, "XDAS", 0, cmdline + 3) == 0)
00182    {
00183       xdasd_log(0, "exec: Error generating temporary batch file name, %ul.\n", 
00184             GetLastError());
00185       return -1;
00186    }
00187    strcat(cmdline, ".bat");
00188    if ((fp = fopen(cmdline, "w")) == 0)
00189    {
00190       xdasd_log(0, "exec: Unable to open temp batch file for write, %d - %s.\n", 
00191             errno, strerror(errno));
00192       return -1;
00193    }
00194    fputs(script, fp);
00195    fclose(fp);
00196    
00197    memset(&sui, 0, sizeof(sui));
00198    sui.cb = sizeof(sui);
00199    if (CreateProcess(shell, cmdline, 0, 0, TRUE, 0, 0, 0, &sui, &pi) == FALSE)
00200    {
00201       xdasd_log(0, "exec: Error creating child process, %ul.\n", GetLastError());
00202       pecode = (DWORD)(-1);
00203    }
00204    else 
00205    {
00206       if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0)
00207          xdasd_log(0, "exec: Error waiting for child process, %ul.\n", 
00208                GetLastError());
00209       else if (GetExitCodeProcess(pi.hProcess, &pecode) == FALSE)
00210          xdasd_log(0, "exec: Error retrieving child process status code, %ul.\n", 
00211                GetLastError());
00212       CloseHandle(pi.hProcess);
00213       CloseHandle(pi.hThread);
00214    }
00215    _unlink(cmdline + 3);
00216    return (int)pecode;
00217 
00218 # else   /* !0 */
00219 
00220    /* This is the process stdin/stdout redirection method - this method 
00221     * words on NT and XP because cmd.exe is now smart enough (finally) to 
00222     * accept stdin from a file (or pipe) and treat it as if it were a 
00223     * script file specified on the command line after a /c option.
00224     */
00225    SECURITY_ATTRIBUTES sa;
00226    PROCESS_INFORMATION pi;
00227    STARTUPINFOA sui;
00228    HANDLE hinrd = INVALID_HANDLE_VALUE;
00229    HANDLE hinwr = INVALID_HANDLE_VALUE;
00230    HANDLE houtrd = INVALID_HANDLE_VALUE;
00231    HANDLE houtwr = INVALID_HANDLE_VALUE;
00232    HANDLE hcurproc = GetCurrentProcess();
00233    DWORD pecode = (DWORD)(-1);
00234    BOOL status;
00235    char * shell;
00236 
00237    /* get the SHELL (COMSPEC) environment variable */
00238    if ((shell = getenv(SHELL_ENVAR)) == 0)
00239       shell = DEFAULT_SHELL;
00240 
00241    /* set pipe security attributes to allow all handles to be inherited */
00242    memset(&sa, 0, sizeof(sa));
00243    sa.nLength = sizeof(sa);
00244    sa.bInheritHandle = TRUE;
00245 
00246    /* create a pipe for the child process's stdout */
00247    if ((status = CreatePipe(&houtrd, &houtwr, &sa, 0)) != FALSE)
00248    {
00249       /* switch to noninheritable read handle */
00250       HANDLE htmp;
00251       if ((status = DuplicateHandle(hcurproc, houtrd, hcurproc, 
00252             &htmp, 0, FALSE, DUPLICATE_SAME_ACCESS)) != FALSE)
00253       {
00254          CloseHandle(houtrd);
00255          houtrd = htmp;
00256       }
00257    }
00258    if (!status)
00259    {
00260       xdasd_log(0, "exec: Error creating child process STDOUT pipe, %ul.\n", 
00261             GetLastError());
00262       goto errout;
00263    }
00264 
00265    /* create a pipe for the child process's stdin */ 
00266    if ((status = CreatePipe(&hinrd, &hinwr, &sa, 0)) != FALSE)
00267    {
00268       /* switch to noninheritable write handle */
00269       HANDLE htmp;
00270       if ((status = DuplicateHandle(hcurproc, hinwr, hcurproc, 
00271             &htmp, 0, FALSE, DUPLICATE_SAME_ACCESS)) != FALSE)
00272       {
00273          CloseHandle(hinwr);
00274          hinwr = htmp;
00275       }
00276    }
00277    if (!status)
00278    {
00279       xdasd_log(0, "exec: Error creating child process STDIN pipe, %ul.\n", 
00280             GetLastError());
00281       goto errout;
00282    }
00283 
00284    /* configure startup parameters, including stdin/stdout handles */
00285    memset(&sui, 0, sizeof(sui));
00286    sui.cb = sizeof(sui);
00287    sui.hStdInput = hinrd;
00288    sui.hStdOutput = houtwr;
00289    sui.hStdError = houtwr;
00290    sui.dwFlags |= STARTF_USESTDHANDLES;
00291 
00292    /* create child process */
00293    if (!CreateProcessA(shell, 0, 0, 0, TRUE, 0, 0, 0, &sui, &pi))
00294       xdasd_log(0, "exec: Error creating child process, %ul.\n", 
00295             GetLastError());
00296    else 
00297    {
00298       DWORD bytes_read, bytes_written; 
00299       char buf[1024];
00300 
00301       CloseHandle(hinrd);  hinrd = INVALID_HANDLE_VALUE;
00302       CloseHandle(houtwr); houtwr = INVALID_HANDLE_VALUE;
00303 
00304       /* write the script to the child process */
00305       if (!WriteFile(hinwr, script, (DWORD)strlen(script), &bytes_written, 0))
00306       {
00307          xdasd_log(0, "exec: Error writing script to child process, %ul.\n", 
00308                GetLastError());
00309          goto errout;
00310       }
00311 
00312       /* close the output pipe handle so the child process stops reading
00313          close our side so the pipe is destroyed completely */
00314       CloseHandle(hinwr);  hinwr = INVALID_HANDLE_VALUE;
00315 
00316       /* read output from the child process, and toss it in the bit-bucket */
00317       while (ReadFile(houtrd, buf, sizeof(buf), &bytes_read, 0) && bytes_read)
00318 #  ifdef LOG_TO_STDOUT  /* except during debugging, print it to stdout */
00319          WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, 
00320                bytes_read, &bytes_written, 0)
00321 #  endif
00322          ;
00323 
00324       /* close the read pipe handle and our side as well */
00325       CloseHandle(houtrd); houtrd = INVALID_HANDLE_VALUE;
00326 
00327       pecode = 0; /* from this point on, we don't fail the call on error */
00328 
00329       /* wait for child to terminate, get status code */
00330       if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0)
00331          xdasd_log(0, "exec: Error waiting for child process, %ul.\n", 
00332                GetLastError());
00333       else if (GetExitCodeProcess(pi.hProcess, &pecode) == FALSE)
00334          xdasd_log(0, "exec: Error retrieving child process status "
00335                "code, %ul.\n", GetLastError());
00336 
00337       /* close process and thread handles */
00338       CloseHandle(pi.hProcess);
00339       CloseHandle(pi.hThread);
00340    }
00341 
00342 errout:  /* close all currently open pipe handles, if needed */
00343    if (hinwr  != INVALID_HANDLE_VALUE) CloseHandle(hinwr);
00344    if (houtwr != INVALID_HANDLE_VALUE) CloseHandle(houtwr);
00345    if (hinrd  != INVALID_HANDLE_VALUE) CloseHandle(hinrd);
00346    if (houtrd != INVALID_HANDLE_VALUE) CloseHandle(houtrd);
00347 
00348    return (int)pecode;
00349 
00350 # endif  /* ?0 */
00351 
00352 #else    /* !_WIN32 */
00353 
00354 # define SHELL_ENVAR    "SHELL"
00355 # define DEFAULT_SHELL  "/bin/sh"
00356 
00357    int rv = -1;
00358    pid_t pid;
00359    int fdstdout[2] = {-1, -1};
00360    int fdstdin[2] = {-1, -1};
00361    char * shell;
00362 
00363    if ((shell = getenv(SHELL_ENVAR)) == 0)
00364       shell = DEFAULT_SHELL;
00365 
00366    /* open pipes for child's stdout and stdin channels */
00367    if (pipe(fdstdout) == -1 || pipe(fdstdin) == -1)
00368    {
00369       xdasd_log(0, "exec: Error creating child process pipes, (%d) %s.\n", 
00370             errno, strerror(errno));
00371       close(fdstdout[0]); close(fdstdout[1]);
00372       return -1;
00373    }
00374 
00375    /* fork - create a child process */
00376    if ((pid = fork()) < 0) /* error */
00377    {
00378       xdasd_log(0, "exec: Error creating child process, (%d) %s.\n", 
00379             errno, strerror(errno));
00380       close(fdstdin[0]);  close(fdstdin[1]);
00381       close(fdstdout[0]); close(fdstdout[1]);
00382       return -1;
00383    }
00384 
00385    if (pid > 0)       /* parent process */
00386    {
00387       int status;
00388       int bytes;
00389       char buf[1024];
00390 
00391       /* close the ends we don't need */
00392       close(fdstdin[0]);   /* child's stdin read handle */
00393       close(fdstdout[1]);  /* child's stdout write handle */
00394 
00395       /* write script to child process's STDIN pipe, close pipe */
00396       write(fdstdin[1], script, strlen(script));
00397       close(fdstdin[1]);
00398 
00399       /* read output from child's STDOUT pipe (and toss it), close pipe */
00400       while ((bytes = read(fdstdout[0], buf, sizeof(buf))) > 0)
00401 #  ifdef LOG_TO_STDOUT  /* except during debugging, print it to stdout */
00402          printf("%.*s", bytes, buf)
00403 #  endif
00404          ;
00405 
00406       close(fdstdout[0]);
00407 
00408       /* wait for child */
00409       if (waitpid(pid, &status, 0) >= 0)
00410          rv = WEXITSTATUS(status);
00411    }
00412    else                    /* child process */
00413    {
00414       /* close the pipe ends we don't need */
00415       close(fdstdin[1]);   /* child's stdin write handle */
00416       close(fdstdout[0]);  /* child's stdout read handle */
00417 
00418       /* dup STDIN read pipe handle onto stdin */
00419       if (fdstdin[0] != STDIN_FILENO)
00420       {
00421          int fdtmp = dup2(fdstdin[0], STDIN_FILENO);
00422          close(fdstdin[0]);
00423          if (fdtmp != STDIN_FILENO)
00424             exit(-1);
00425       }
00426 
00427       /* dup STDOUT write pipe handle onto stdout - ignore errors */
00428       if (fdstdout[1] != STDOUT_FILENO)
00429       {
00430          dup2(fdstdout[1], STDOUT_FILENO);
00431          close(fdstdout[1]);
00432       }
00433 
00434       /* dup STDOUT handle onto stderr - ignore errors */
00435       if (STDOUT_FILENO != STDERR_FILENO)
00436          dup2(STDOUT_FILENO, STDERR_FILENO); 
00437 
00438       /* now exec shell process, allow to inherit dup'ed handles */
00439       if (execl(shell, 0) < 0)
00440          exit(-1);
00441 
00442       /* [ == success or failure, we'll never leave this scope alive == ] */
00443    }
00444    return rv;
00445 
00446 #endif   /* ?_WIN32 */
00447 }
00448 
00480 int xdasd_exec(const char * script, const char ** msgflds)
00481 {
00482    int rv;
00483    char * text;
00484 
00485    if ((text = ex_substitute_vars(script, msgflds)) == 0)
00486       return -1;
00487 
00488    rv = ex_exec_script(text);
00489 
00490    free(text);
00491    return rv;
00492 }
00493 
00494 /*==========================================================================*/
00495 
00496 #ifdef XDASD_EXEC_TEST
00497 
00498 #include <stdarg.h>
00499 
00506 void xdasd_log(int level, const char * fmt, ... ) 
00507 {
00508 #ifdef LOG_TO_STDOUT
00509    va_list args;
00510    va_start(args, fmt);
00511    vprintf(fmt, args);
00512    va_end(args);
00513 #else
00514    (void)fmt;
00515 #endif
00516 }
00517 
00538 int main(int argc, char ** argv)
00539 {
00540    static char * script =
00541    {
00542       "echo \"Replace $XDAS with entire message (really long text).\"\n"
00543       "echo \"Replace $XDAS(10) with the 10th field (< than the var).\"\n"
00544       "echo \"Replace $XDAS(11) with the 11th field (> than the var).\""
00545    };
00546    static char * msg = 
00547    /*  0 */ "HDR:"
00548    /*  1 */ "00A8:"
00549    /*  2 */ "OX1:"
00550    /*  3 */ "2234934821:"
00551    /*  4 */ ":"
00552    /*  5 */ ":"
00553    /*  6 */ "time.nist.gov:"
00554    /*  7 */ "MST7MDT:"
00555    /*  8 */ "0x10000001:"
00556    /*  9 */ "0:"
00557    /* 10 */ "ORG:"
00558    /* 11 */ "org_location_name:"
00559    /* 12 */ "org_location_address:"
00560    /* 13 */ "org_service_type:"
00561    /* 14 */ "org_auth_authority:"
00562    /* 15 */ "org_principal_name:"
00563    /* 16 */ "org_principal_id:"
00564    /* 17 */ "INT:"
00565    /* 18 */ "int_auth_authority:"
00566    /* 19 */ "int_domain_specific_name:"
00567    /* 20 */ "int_domain_specific_id:"
00568    /* 21 */ "TGT:"
00569    /* 22 */ "tgt_location_name:"
00570    /* 23 */ "tgt_location_address:"
00571    /* 24 */ "tgt_service_type:"
00572    /* 25 */ "tgt_auth_authority:"
00573    /* 26 */ "tgt_principal_name:"
00574    /* 27 */ "tgt_principal_id:"
00575    /* 28 */ "SRC:"
00576    /* 29 */ "pointer_to_source_domain:"
00577    /* 30 */ "EVT:"
00578    /* 31 */ "event_specific_information:"
00579    /* 32 */ "END";
00580 
00581    unsigned i;
00582    char * cp = msg;
00583    const char * msgflds[XDAS_FIELD_COUNT + 1];
00584 
00585    (void)argc;
00586    (void)argv;
00587 
00588    for (i = 0; i < XDAS_FIELD_COUNT; i++)
00589    {
00590       msgflds[i] = cp;
00591       while (*cp && *cp != ':')
00592          cp++;
00593       cp++;
00594    }
00595    msgflds[i] = cp;
00596 
00597    return xdasd_exec(script, msgflds);
00598 }
00599 
00600 #endif
00601 

Generated on Thu Aug 20 22:33:05 2009 for OpenXDAS by  doxygen 1.5.6