1/*
2* Copyright (C) 2011 The Android Open Source Project
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*/
16#include "osProcess.h"
17#include <stdio.h>
18#include <stdlib.h>
19#include <sys/wait.h>
20#include <sys/types.h>
21#include <poll.h>
22#include <pthread.h>
23#include <string.h>
24#include <pwd.h>
25#include <paths.h>
26#include <errno.h>
27#include <signal.h>
28#include <unistd.h>
29#include <assert.h>
30
31namespace osUtils {
32
33//
34// buildArgList converts a command line into null terminated argument list.
35// to be used with execv or execvp.
36// each argument is seperated by space or tab, to specify multiple words
37// at the same argument place it inside single-quoted or double-quoted string.
38//
39static char **buildArgList(const char *command)
40{
41    char **argv = NULL;
42    int argvSize = 0;
43    int nArgs = 0;
44    char *tmpcmd = strdup(command);
45    char *t = tmpcmd;
46    char *strStart = NULL;
47    int i = 0;
48
49    #define ADD_ARG \
50        { \
51            nArgs++; \
52            if (!argv) { \
53                argvSize = 12; \
54                argv = (char **)malloc(argvSize * sizeof(char *)); \
55            } \
56            else if (nArgs > argvSize) { \
57                argvSize += 12; \
58                argv = (char **)realloc(argv, argvSize * sizeof(char *)); \
59            } \
60            argv[nArgs-1] = t; \
61            t = NULL; \
62        }
63
64    while( tmpcmd[i] != '\0' ) {
65        if (!strStart) {
66            if (tmpcmd[i] == '"' || tmpcmd[i] == '\'') {
67                strStart = &tmpcmd[i];
68            }
69            else if (tmpcmd[i] == ' ' || tmpcmd[i] == '\t') {
70                tmpcmd[i] = '\0';
71                if (t) ADD_ARG;
72            }
73            else if (!t) {
74                t = &tmpcmd[i];
75            }
76        }
77        else if (tmpcmd[i] == *strStart) {
78            t = strStart;
79            strStart = NULL;
80        }
81
82        i++;
83    }
84    if (t) {
85        ADD_ARG;
86    }
87    if (nArgs > 0) {
88        ADD_ARG; // for NULL terminating list
89    }
90
91    return argv;
92}
93
94static pid_t start_process(const char *command,const char *startDir)
95{
96    pid_t pid;
97
98    pid = fork();
99
100    if (pid < 0) {
101        return pid;
102    }
103    else if (pid == 0) {
104        //
105        // Close all opened file descriptors
106        //
107        for (int i=3; i<256; i++) {
108            close(i);
109        }
110
111        if (startDir) {
112            chdir(startDir);
113        }
114
115        char **argv = buildArgList(command);
116        if (!argv) {
117            return -1;
118        }
119        execvp(argv[0], argv);
120
121        perror("execl");
122        exit(-101);
123    }
124
125    return pid;
126}
127
128childProcess *
129childProcess::create(const char *p_cmdLine, const char *p_startdir)
130{
131    childProcess *child = new childProcess();
132    if (!child) {
133        return NULL;
134    }
135
136    child->m_pid = start_process(p_cmdLine, p_startdir);
137    if (child->m_pid < 0) {
138        delete child;
139        return NULL;
140    }
141
142    return child;
143}
144
145childProcess::~childProcess()
146{
147}
148
149bool
150childProcess::wait(int *exitStatus)
151{
152    int ret=0;
153    if (m_pid>0) {
154        pid_t pid = waitpid(m_pid,&ret,0);
155        if (pid != -1) {
156            m_pid=-1;
157            if (exitStatus) {
158                *exitStatus = ret;
159            }
160            return true;
161        }
162    }
163    return false;
164}
165
166int
167childProcess::tryWait(bool &isAlive)
168{
169    int ret=0;
170    isAlive = false;
171    if (m_pid>0) {
172        pid_t pid = waitpid(m_pid,&ret,WNOHANG);
173        if (pid == 0) {
174            isAlive = true;
175        }
176    }
177
178    return ((char)WEXITSTATUS(ret));
179}
180
181int ProcessGetPID()
182{
183    return getpid();
184}
185
186int KillProcess(int pid, bool wait)
187{
188    if (pid<1) {
189        return false;
190    }
191
192    if (0!=kill(pid,SIGTERM)) {
193        return false;
194    }
195
196    if (wait) {
197        if (waitpid(pid,NULL,0)<0) {
198            return false;
199        }
200    }
201
202    return true;
203}
204
205bool isProcessRunning(int pid)
206{
207    return (kill(pid,0) == 0);
208}
209
210} // of namespace osUtils
211