1 /* CONSPAWN.C (c) "Fish" (David B. Trout), 2005-2012 */
2 /* Spawn console command */
3
4 /*-------------------------------------------------------------------*/
5 /* This program is spawned by Hercules as a result of */
6 /* the 'sh' (shell) command. Its purpose is to simply */
7 /* call the host's shell (command interpreter) program */
8 /* with the arguments supplied (usually to invoke yet */
9 /* another program), redirecting the results back to */
10 /* Hercules for display. NOTE that this program does */
11 /* not perform the actual stdio redirection itself, but */
12 /* rather relies on Hercules to have set that up before */
13 /* invoking this program. */
14 /*-------------------------------------------------------------------*/
15
16 #include "hstdinc.h"
17
18 #define PGMNAME "conspawn"
19
main(int argc,char * argv[])20 int main(int argc, char* argv[])
21 {
22 int i, k, rc;
23 char* p;
24
25 #ifdef _MSVC_
26 #pragma comment(lib,"shell32.lib") // (need ShellExecute)
27 // --------------------------------------------------------
28 // PROGRAMMING NOTE: We MUST use "ShellExecute" for Windows
29 // GUI programs since: 1) GUI programs don't use stdio,
30 // 2) they never exit until the user manually closes them.
31 //
32 // Erroneously using the 'system()' function to start a GUI
33 // program causes HercGUI to hang at PowerOff as it waits
34 // for its child process to close its stdio handles which
35 // GUI programs never do until they exit (which they never
36 // do until the user manually closes them).
37 //
38 // The reason this phenomenon occurs even though Hercules
39 // does indeed close ITS OWN stdio handles when it ends is
40 // because its child processes that it creates ALSO inherit
41 // the same stdio handles! (I.e. the GUI programs that Herc
42 // starts end up never closing "Herc's" [inherited] stdio
43 // handles, which are the same handles that HercGUI waits
44 // on! Thus GUI programs started by Herc using the 'system'
45 // API end up hanging HercGUI! (during Herc PowerOff))
46 //
47 // Thus, for GUI apps, we MUST use "ShellExecute" here
48 // to prevent HercGUI from hanging at PowerOff. Also note
49 // that this hang obviously does not occur when Hercules
50 // is run in command-line (non-HercGUI) mode even when
51 // the 'system()' API is erroneously used, since Herc is
52 // obviously (duh!) not being run under the control of an
53 // external GUI in such a situation. -- Fish, Aug. 2006
54 // --------------------------------------------------------
55
56 if (argc >= 2 && strcasecmp(argv[1],"startgui") == 0)
57 {
58 ////////////////////////////////////////////////////////
59 // Windows GUI program; no stdio; use 'ShellExecute'...
60 ////////////////////////////////////////////////////////
61
62 // REFERENCE: upon entry:
63
64 // argv[0] "conspawn"
65 // argv[1] "startgui"
66 // argv[2] (program to start)
67 // argv[3..n] (arguments for program)
68
69 HWND hwnd = NULL;
70 LPCTSTR lpOperation = NULL;
71 LPCTSTR lpFile = argv[2];
72 LPCTSTR lpParameters = NULL;
73 LPCTSTR lpDirectory = NULL;
74 INT nShowCmd = SW_SHOWNORMAL;
75 char* pszErrMsg = NULL;
76
77 // Build arguments string from passed args...
78
79 for (i=3, k=0; i < argc; i++)
80 k += strlen(argv[i]) + 1;
81
82 if (k)
83 {
84 // (allocate room for arguments string)
85
86 if (!(p = malloc(sizeof(char)*k)))
87 {
88 errno = ENOMEM;
89 perror( PGMNAME );
90 return -1;
91 }
92 *p = 0;
93
94 // (build arguments string from args)
95
96 for (i=3; i < argc; ++i)
97 {
98 strcat(p,argv[i]);
99 if (i != (argc-1)) strcat(p," ");
100 }
101
102 lpParameters = p;
103 }
104 else
105 p = NULL;
106
107 rc = (intptr_t) ShellExecute( hwnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd );
108
109 if (p)
110 free(p);
111
112 if ( rc > 32)
113 return 0; // rc > greater than 32 == success...
114
115 // rc <= less than or equal 32 == error...
116
117 switch (rc)
118 {
119 case 0: pszErrMsg = "The operating system is out of memory or resources"; break;
120 case SE_ERR_FNF: pszErrMsg = "The specified file was not found"; break;
121 case SE_ERR_PNF: pszErrMsg = "The specified path was not found"; break;
122 case SE_ERR_ACCESSDENIED: pszErrMsg = "The operating system denied access to the specified file"; break;
123 case ERROR_BAD_FORMAT: pszErrMsg = "The .exe file is invalid (non-Microsoft Win32 .exe or error in .exe image)"; break;
124 case SE_ERR_OOM: pszErrMsg = "There was not enough memory to complete the operation"; break;
125 case SE_ERR_DLLNOTFOUND: pszErrMsg = "The specified dynamic-link library (DLL) was not found"; break;
126 case SE_ERR_SHARE: pszErrMsg = "A sharing violation occurred"; break;
127 case SE_ERR_ASSOCINCOMPLETE: pszErrMsg = "The file name association is incomplete or invalid"; break;
128 case SE_ERR_DDETIMEOUT: pszErrMsg = "The DDE transaction could not be completed because the request timed out"; break;
129 case SE_ERR_DDEFAIL: pszErrMsg = "The DDE transaction failed"; break;
130 case SE_ERR_DDEBUSY: pszErrMsg = "The Dynamic Data Exchange (DDE) transaction could not be completed because other DDE transactions were being processed"; break;
131 case SE_ERR_NOASSOC: pszErrMsg = "There is no application associated with the given file name extension. This error will also be returned if you attempt to print a file that is not printable"; break;
132
133 default:
134 printf(PGMNAME": ShellExecute(\"%s\", \"%s\",...) failed: Unknown error; rc=%d (0x%08.8X).\n",
135 lpFile, lpParameters, rc, rc );
136 return -1;
137 }
138
139 printf( PGMNAME": ShellExecute(\"%s\", \"%s\",...) failed: %s.\n",
140 lpFile, lpParameters, pszErrMsg );
141
142 return -1;
143 }
144 #endif // _MSVC_
145
146 ////////////////////////////////////////////////////////
147 // Command line program using stdio; use 'system()'...
148 ////////////////////////////////////////////////////////
149
150 // Re-build a complete command line from passed args...
151
152 for (i=1, k=0; i < argc; i++)
153 k += strlen(argv[i]) + 1;
154
155 if (!k)
156 {
157 errno = EINVAL;
158 perror( PGMNAME );
159 printf( PGMNAME": Usage: command [args]\n");
160 return -1;
161 }
162
163 // (allocate room for command line)
164
165 if (!(p = malloc(sizeof(char)*k)))
166 {
167 errno = ENOMEM;
168 perror( PGMNAME );
169 return -1;
170 }
171 *p = 0;
172
173 // (rebuild command-line from args)
174
175 for (i=1; i < argc; ++i)
176 {
177 strcat(p,argv[i]);
178 if (i != (argc-1)) strcat(p," ");
179 }
180
181 // Ask system() to process command line...
182
183 // NOTE: the below call WILL NOT RETURN
184 // until the program being called exits!
185
186 rc = system(p);
187
188 free(p);
189
190 // --------------------------------------------------------
191 // PROGRAMMING NOTE: only rc == -1 need be reported since,
192 // if the command interpreter called a batch/cmd file for
193 // example, it could have set its own custom return code.
194 //
195 // Only -1 means the system() call itself failed, which
196 // is the only thing that actually needs to be reported.
197 // --------------------------------------------------------
198
199 if ( -1 == rc )
200 perror( PGMNAME );
201
202 return rc;
203 }
204