Ticket #461: spawn_process.diff

File spawn_process.diff, 10.2 KB (added by tryagain, 10 years ago)

Alpha version of spawn_process.

  • main.c

     
    344344#endif
    345345
    346346#ifndef _WIN32
    347         signal(SIGCHLD, sigchld);
     347//      signal(SIGCHLD, sigchld);
    348348#endif
    349349        cbl=callback_list_new();
    350350#ifdef HAVE_API_WIN32_BASE
  • util.c

     
    2323#include <stdarg.h>
    2424#include <time.h>
    2525#include <limits.h>
     26#include <string.h>
     27
     28#ifdef _POSIX_C_SOURCE
     29#include <unistd.h>
     30#include <sys/types.h>
     31#include <sys/wait.h>
     32#endif
    2633#include "util.h"
     34#include "debug.h"
    2735
    2836void
    2937strtoupper(char *dest, const char *src)
     
    344352#endif
    345353        return timep;
    346354}
     355
     356
     357struct spawn_process_info {
     358#ifdef HAVE_API_WIN32_BASE
     359        PROCESS_INFORMATION pr;
     360#else
     361        pid_t pid; // = -1 if non-blocking spawn isn't supported
     362        int status; // exit status if non-blocking spawn isn't supported
     363#endif
     364};
     365
     366
     367/**
     368 * Escape and quote string for shell
     369 *
     370 * @param in arg string to escape
     371 * @returns escaped string
     372 */
     373char *
     374shell_escape(char *arg)
     375{
     376        char *r;
     377        int arglen=strlen(arg);
     378        int i,j,rlen;
     379#ifdef HAVE_API_WIN32_BASE
     380        {
     381                int bscount=0;
     382                rlen=arglen+3;
     383                r=g_new(char,rlen);
     384                r[0]='"';
     385                for(i=0,j=1;i<arglen;i++) {
     386                        if(arg[i]=='\\') {
     387                                bscount++;
     388                                if(i==(arglen-1)) {
     389                                        // Most special case - last char is
     390                                        // backslash. We can't escape it inside
     391                                        // quoted string due to Win unescaping
     392                                        // rules so quote should be closed
     393                                        // before backslashes and these
     394                                        // backslashes shouldn't be doubled
     395                                        rlen+=bscount;
     396                                        r=g_realloc(r,rlen);
     397                                        r[j++]='"';
     398                                        memset(r+j,'\\',bscount);
     399                                        j+=bscount;
     400                                }
     401                        } else {
     402                                //Any preceeding backslashes will be doubled.
     403                                bscount*=2;
     404                                // Double quote needs to be preceeded by
     405                                // at least one backslash
     406                                if(arg[i]=='"')
     407                                        bscount++;
     408                                if(bscount>0) {
     409                                        rlen+=bscount;
     410                                        r=g_realloc(r,rlen);
     411                                        memset(r+j,'\\',bscount);
     412                                        j+=bscount;
     413                                        bscount=0;
     414                                }
     415                                r[j++]=arg[i];
     416                                if(i==(arglen-1)) {
     417                                        r[j++]='"';
     418                                }
     419                        }
     420                }
     421                r[j++]=0;
     422        }
     423#else
     424        {
     425                // Will use hard quoting for the whole string
     426                // and replace each singular quote found with a '\'' sequence.
     427                rlen=arglen+3;
     428                r=g_new(char,rlen);
     429                r[0]='\'';
     430                for(i=0,j=1;i<arglen;i++) {
     431                        if(arg[i]=='\'') {
     432                                rlen+=3;
     433                                r=g_realloc(r,rlen);
     434                                strcpy(r+j,"'\\''");
     435                        } else {
     436                                r[j++]=arg[i];
     437                        }
     438                }
     439                r[j++]='\'';
     440                r[j++]=0;
     441        }
     442#endif
     443        return r;
     444}
     445
     446#ifndef _POSIX_C_SOURCE____
     447static char*
     448spawn_process_compose_cmdline(char **argv)
     449{
     450        int i,j;
     451        char *cmdline=shell_escape(argv[0]);
     452        for(i=1,j=strlen(cmdline);argv[i];i++) {
     453                char *arg=shell_escape(argv[i]);
     454                int arglen=strlen(arg);
     455                cmdline[j]=' ';
     456                cmdline=g_realloc(cmdline,j+1+arglen+1);
     457                memcpy(cmdline+j+1,arg,arglen+1);
     458                g_free(arg);
     459                j=j+1+arglen;
     460        }
     461        return cmdline;
     462}
     463#endif
     464
     465/**
     466 * Call external program
     467 *
     468 * @param in argv NULL terminated list of parameters,
     469 *    zeroeth argument is program name
     470 * @returns 0 - success, >0 - return code, -1 - error
     471 */
     472struct spawn_process_info*
     473spawn_process(char **argv)
     474{
     475        struct spawn_process_info*r=g_new(struct spawn_process_info,1);
     476#ifdef _POSIX_C_SOURCE
     477        {
     478                pid_t pid=fork();
     479                if(pid==0) {
     480                        execvp(argv[0], argv);
     481                        /*Shouldn't reach here*/
     482                        exit(1);
     483                } else if(pid>0) {
     484                        r->pid=pid;
     485                        return r;
     486                } else {
     487                        dbg(0,"fork() returned error.");
     488                        g_free(r);
     489                        return NULL;
     490                }
     491        }
     492#else
     493#ifdef HAVE_API_WIN32_BASE
     494        {
     495                char *cmdline;
     496                LPCWSTR cmd,args;
     497                DWORD dwRet;
     498
     499                // For [desktop] Windows it's adviceable not to use
     500                // first CreateProcess parameter because if it defined
     501                // PATH is not used.
     502                // On WinCE 6.0 I was unable to launch anything
     503                // without first CreateProcess parameter. So...
     504#ifdef HAVE_API_WIN32_CE
     505                cmdline=spawn_process_compose_cmdline(argv+1);
     506                args=newSysString(cmdline);
     507                cmd = newSysString(argv[0]);
     508                dwRet=CreateProcess(cmd, args, NULL, NULL, 0, 0, NULL, NULL, NULL, &(r->pr));
     509                dbg(0, "CreateProcess(%s,%s), PID=%i\n",argv[0],cmdline,r->pr.dwProcessId);
     510                g_free(cmd);
     511#else
     512                cmdline=spawn_process_compose_cmdline(argv);
     513                args=newSysString(cmdline);
     514                dwRet=CreateProcess(NULL, args, NULL, NULL, 0, 0, NULL, NULL, NULL, &(r->pr));
     515                dbg(0, "CreateProcess(%s), PID=%i\n",cmdline,r->pr.dwProcessId);
     516#endif
     517                g_free(cmdline);
     518                g_free(args);
     519                return r;
     520        }
     521#else
     522        {
     523                char *cmdline=spawn_process_compose_cmdline(argv);
     524                int status;
     525                dbg(0,"Unblocked spawn_process isn't availiable on this platform.\n");
     526                status=system(cmdline);
     527                g_free(cmdline);
     528                r->status=status;
     529                r->pid=0;
     530                return r;
     531        }
     532#endif
     533#endif
     534}
     535
     536/**
     537 * Check external program status
     538 *
     539 * @param in *pi pointer to spawn_process_info structure
     540 * @param in block =0 do not block =1 block until child terminated
     541 * @returns -1 - still running, >=0 program exited,
     542 *     =255 trminated abnormally or wasn't run at all.
     543 *
     544 */
     545int spawn_process_check_status(struct spawn_process_info *pi, int block)
     546{
     547        if(pi==NULL) {
     548                dbg(0,"Trying to get process status of NULL, assuming process is terminated.\n");
     549                return 255;
     550        }
     551#ifdef HAVE_API_WIN32_BASE
     552        while(1){
     553                DWORD dw;
     554                if(GetExitCodeProcess(pi->pr.hProcess,&dw)) {
     555                        if(dw!=STILL_ACTIVE) {
     556                                return dw;
     557                                break;
     558                        }
     559                } else {
     560                        dbg(0,"GetExitCodeProcess failed. Assuming the process is terminated.");
     561                        return 255;
     562                }
     563                if(!block)
     564                        return -1;
     565               
     566                dw=WaitForSingleObject(pi->pr.hProcess,INFINITE);
     567                if(dw==WAIT_FAILED) {
     568                        dbg(0,"WaitForSingleObject failed. Assuming the process is terminated.");
     569                        return 0;
     570                        break;
     571                }
     572        }
     573#else
     574#ifdef _POSIX_C_SOURCE
     575        while(1) {
     576                int status;
     577                pid_t w=waitpid(pi->pid,&status,block?0:WNOHANG);
     578                if(w>=0) {
     579                        if(WIFEXITED(status))
     580                                return WEXITSTATUS(status);
     581                        if(WIFSTOPPED(status)) {
     582                                dbg(0,"child is stopped by %i signal\n",WSTOPSIG(status));
     583                        } else if (WIFSIGNALED(status)) {
     584                                dbg(0,"child terminated by signal %i\n",WEXITSTATUS(status));
     585                                return 255;
     586                        }
     587                        if(!block)
     588                                return -1;
     589                } else {
     590                        dbg(0,"waitpid() indicated error, reporting process termination.\n");
     591                        perror();
     592                        return 255;
     593                }
     594        }
     595#else
     596        dbg(0, "Non-blocking spawn_process isn't availiable for this platform, repoting process exit status.\n");
     597        return pi->status;
     598#endif
     599#endif
     600}
     601
     602void spawn_process_info_free(struct spawn_process_info *pi)
     603{
     604        if(pi==NULL)
     605                return;
     606#ifdef HAVE_API_WIN32_BASE
     607        CloseHandle(pi->pr.hProcess);
     608        CloseHandle(pi->pr.hThread);
     609#endif
     610        g_free(pi);
     611}
     612
     613
  • util.h

     
    4545
    4646#endif
    4747
     48struct spawn_process_info;
     49char * shell_escape(char *arg);
     50struct spawn_process_info* spawn_process(char **argv);
     51int spawn_process_check_status(struct spawn_process_info *pi,int block);
     52void spawn_process_info_free(struct spawn_process_info *pi);
     53
    4854#endif
    4955
  • navit.c

     
    6666#include "vehicleprofile.h"
    6767#include "sunriset.h"
    6868#include "bookmarks.h"
     69#ifdef HAVE_API_WIN32_BASE
     70#include <windows.h>
     71#include "util.h"
     72#endif
    6973
    7074/**
    7175 * @defgroup navit the navit core instance. navit is the object containing nearly everything: A set of maps, one or more vehicle, a graphics object for rendering the map, a gui object for displaying the user interface, a route object, a navigation object and so on. Be warned that it is theoretically possible to have more than one navit object
     
    941945        }
    942946}
    943947
     948/**
     949 * Join several string attributes into one
     950 *
     951 * @param navit The navit instance
     952 * @param function unused (needed to match command function signiture)
     953 * @param in input attributes in[0] - separator, in[1..] - attributes to join
     954 * @param out output attribute joined attribute as string
     955 * @param valid unused
     956 * @returns nothing
     957 */
     958static void
     959navit_cmd_strjoin(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
     960{
     961        struct attr attr;
     962        gchar *ret, *sep;
     963        int i;
     964        attr.type=attr_type_string_begin;
     965        attr.u.str=NULL;
     966        if(in[0] && in[1]) {
     967                sep=attr_to_text(in[0],NULL,1);
     968                ret=attr_to_text(in[1],NULL,1);
     969                for(i=2;in[i];i++) {
     970                        gchar *in_i=attr_to_text(in[i],NULL,1);
     971                        gchar *r=g_strjoin(sep,ret,in_i,NULL);
     972                        g_free(in_i);
     973                        g_free(ret);
     974                        ret=r;
     975                }
     976                g_free(sep);
     977                attr.u.str=ret;
     978                if(out) {
     979                        *out=attr_generic_add_attr(*out, &attr);
     980                }
     981                g_free(ret);
     982        }
     983}
     984
     985/**
     986 * Call external program
     987 *
     988 * @param navit The navit instance
     989 * @param function unused (needed to match command function signiture)
     990 * @param in input attributes in[0] - name of executable, in[1..] - parameters
     991 * @param out output attribute unused
     992 * @param valid unused
     993 * @returns nothing
     994 */
     995static void
     996navit_cmd_spawn(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
     997{
     998        int i,j, nparms, nvalid;
     999        const char ** argv=NULL;
     1000        struct spawn_process_info *pi;
     1001
     1002        nparms=0;
     1003        nvalid=0;
     1004        if(in) {
     1005                while(in[nparms]) {
     1006                        if (in[nparms]->type!=attr_none)
     1007                                nvalid++;
     1008                        nparms++;
     1009                }
     1010        }
     1011       
     1012        if(nvalid>0) {
     1013                argv=g_new(char*,nvalid+1);
     1014                for(i=0,j=0;in[i];i++) {
     1015                        if(in[i]->type!=attr_none ) {
     1016                                argv[j++]=attr_to_text(in[i],NULL,1);
     1017                        } else {
     1018                                dbg(0,"Parameter #%i is attr_none - skipping\n",i);
     1019                        }
     1020                }
     1021                argv[j]=NULL;
     1022                pi=spawn_process(argv);
     1023                int st=spawn_process_check_status(pi,0);
     1024                dbg(0,"status %i\n",st);
     1025                st=spawn_process_check_status(pi,1);
     1026                dbg(0,"status %i\n",st);
     1027                spawn_process_info_free(pi);
     1028                for(i=0;argv[i];i++)
     1029                g_free(argv[i]);
     1030                g_free(argv);
     1031        }
     1032}
     1033
     1034
    9441035static struct command_table commands[] = {
    9451036        {"zoom_in",command_cast(navit_cmd_zoom_in)},
    9461037        {"zoom_out",command_cast(navit_cmd_zoom_out)},
     
    9561047        {"pop_int",command_cast(navit_cmd_pop_int)},
    9571048        {"int_stack_size",command_cast(navit_cmd_int_stack_size)},
    9581049        {"toggle_layer",command_cast(navit_cmd_toggle_layer)},
     1050        {"strjoin",command_cast(navit_cmd_strjoin)},
     1051        {"spawn",command_cast(navit_cmd_spawn)},
    9591052};
    9601053       
    9611054void