1/*
2 * This file is part of Red Panda C++
3 * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#include <string>
20#include <vector>
21using std::string;
22using std::vector;
23#include <string.h>
24#include <stdio.h>
25#include <sys/mman.h>
26#include <sys/types.h>
27#include <unistd.h>
28#include <sys/stat.h> /* For mode constants */
29#include <fcntl.h> /* For O_* constants */
30#include <chrono>
31#include <sys/wait.h>
32#define MAX_COMMAND_LENGTH 32768
33#define MAX_ERROR_LENGTH 2048
34
35enum RunProgramFlag {
36 RPF_PAUSE_CONSOLE = 0x0001,
37 RPF_REDIRECT_INPUT = 0x0002
38};
39
40
41void PauseExit(int exitcode, bool reInp) {
42 if (reInp) {
43 freopen("/dev/tty","r",stdin);
44 }
45 fflush(stdin);
46 printf("\n");
47 printf("Press ANY key to exit...");
48 getchar();
49 exit(exitcode);
50}
51
52vector<string> GetCommand(int argc,char** argv,bool &reInp,bool &pauseAfterExit) {
53 vector<string> result;
54 int flags = atoi(argv[1]);
55 reInp = flags & RPF_REDIRECT_INPUT;
56 pauseAfterExit = flags & RPF_PAUSE_CONSOLE;
57 for(int i = 3;i < argc;i++) {
58 //result += string("\"") + string(argv[i]) + string("\"");
59 std::string s(argv[i]);
60
61 if (i==3 || (reInp && i==4 ))
62 if (s.length()>2 && s[0]=='\"' && s[s.length()-1]=='\"') {
63 s = s.substr(1,s.length()-2);
64 }
65 result.push_back(s);
66 }
67
68 return result;
69}
70
71int ExecuteCommand(vector<string>& command,bool reInp) {
72 pid_t pid = fork();
73 if (pid == 0) {
74 string path_to_command;
75 char * * argv;
76 int command_begin;
77 int command_size;
78 if (reInp) {
79 if (command.size()<2) {
80 printf("not enough arguments1!\n");
81 exit(-1);
82 }
83 freopen(command[0].c_str(),"r",stdin);
84 path_to_command = command[1];
85 command_size = command.size()+1;
86 command_begin = 1;
87 } else {
88 if (command.size()<1) {
89 printf("not enough arguments2!\n");
90 exit(-1);
91 }
92 path_to_command = command[0];
93 command_size = command.size()+1;
94 command_begin = 0;
95 }
96 argv = (char * *)malloc(sizeof(char *)*command_size);
97 for (int i=command_begin;i<command.size();i++) {
98 argv[i-command_begin] = (char *)command[i].c_str();
99 }
100 argv[command.size()-command_begin]=NULL;
101 //child process
102 int pos = path_to_command.find_last_of('/');
103 std::string file = path_to_command;
104 if (pos>=0) {
105 file = path_to_command.substr(pos+1);
106 }
107 argv[0]=(char *)file.c_str();
108 int result=execv(path_to_command.c_str(),argv);
109 if (result) {
110 printf("Failed to start command %s %s!\n",path_to_command.c_str(), file.c_str());
111 printf("errno %d: %s\n",errno,strerror(errno));
112 char* current_dir = getcwd(nullptr, 0);
113 printf("current dir: %s",current_dir);
114 free(current_dir);
115 exit(-1);
116 }
117 } else {
118 int status;
119 pid_t w;
120 w = waitpid(pid, &status, WUNTRACED | WCONTINUED);
121 if (w==-1) {
122 perror("waitpid failed!");
123 exit(EXIT_FAILURE);
124 }
125 if (WIFEXITED(status)) {
126 return WEXITSTATUS(status);
127 } else {
128 return status;
129 }
130 }
131 return 0;
132}
133
134int main(int argc, char** argv) {
135 char* sharedMemoryId;
136 // First make sure we aren't going to read nonexistent arrays
137 if(argc < 4) {
138 printf("\n--------------------------------");
139 printf("\nUsage: ConsolePauser.exe <0|1> <shared_memory_id> <filename> <parameters>\n");
140 printf("\n 1 means the STDIN is redirected by Red Panda C++; 0 means not\n");
141 PauseExit(EXIT_SUCCESS,false);
142 }
143
144 // Make us look like the paused program
145 //SetConsoleTitleA(argv[3]);
146 sharedMemoryId = argv[2];
147
148 bool reInp;
149 bool pauseAfterExit;
150 // Then build the to-run application command
151 vector<string> command = GetCommand(argc,argv,reInp, pauseAfterExit);
152 if (reInp) {
153 freopen("/dev/tty","w+",stdout);
154 freopen("/dev/tty","w+",stderr);
155 } else {
156 fflush(stdin);
157 }
158
159 int BUF_SIZE=1024;
160 char* pBuf=nullptr;
161 int fd_shm = shm_open(sharedMemoryId,O_RDWR,S_IRWXU);
162 if (fd_shm==-1) {
163 //todo: handle error
164 printf("shm open failed %d:%s\n",errno,strerror(errno));
165 } else {
166 if (ftruncate(fd_shm,BUF_SIZE)==-1){
167 printf("ftruncate failed %d:%s\n",errno,strerror(errno));
168 //todo: set size error
169 } else {
170 pBuf = (char*)mmap(NULL,BUF_SIZE,PROT_READ | PROT_WRITE, MAP_SHARED, fd_shm,0);
171 if (pBuf == MAP_FAILED) {
172 printf("mmap failed %d:%s\n",errno,strerror(errno));
173 pBuf = nullptr;
174 }
175 }
176 }
177
178 // Save starting timestamp
179 auto starttime = std::chrono::high_resolution_clock::now();
180
181 // Execute the command
182 int returnvalue = ExecuteCommand(command,reInp);
183
184 // Get ending timestamp
185 auto endtime = std::chrono::high_resolution_clock::now();
186 auto difftime = endtime - starttime;
187 auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(difftime);
188 double seconds = milliseconds.count()/1000;
189
190 if (pBuf) {
191 strcpy(pBuf,"FINISHED");
192 munmap(pBuf,BUF_SIZE);
193 }
194 if (fd_shm!=-1) {
195 shm_unlink(sharedMemoryId);
196 }
197
198 // Done? Print return value of executed program
199 printf("\n--------------------------------");
200 printf("\nProcess exited after %.4g seconds with return value %lu\n",seconds,returnvalue);
201 if (pauseAfterExit)
202 PauseExit(returnvalue,reInp);
203 return 0;
204}
205
206