summaryrefslogtreecommitdiffstats
path: root/funtools/util/winprocess.c
blob: 70839dc21f2c66aec957937d63799a26d8dfca43 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
/*
 *	Copyright (c) 2009 Smithsonian Astrophysical Observatory
 */

/* 
 *
 * winprocess.c -- routines to start up and communicate with a slave process
 *
 * this is the windows version, see zprocess.c for original code
 *
 */

#include <winprocess.h> 

#if defined(HAVE_CYGWIN) || defined(WIN32)

#include <windows.h>

#ifndef min
#define	min(a,b)	(((a)<(b))?(a):(b))
#endif

/*
 *
 * Private Routines
 *
 *
 */

#define MAXPROCS 512

typedef struct wproctable {
  HANDLE hIchan;
  HANDLE hOchan;
  HANDLE hProcess;
} WPROC;


static WPROC wprtable[MAXPROCS];

/* WPR_FINDPROCESS -- Search the process table for a process */
static WPROC *wpr_findprocess(HANDLE process)
{
  register int wpr;
  for(wpr=0; wpr<MAXPROCS; wpr++){
    if(wprtable[wpr].hProcess == process){
      return (&wprtable[wpr]);
    }
  }
  return (NULL);
}

/* WPR_ENTER -- Make a new entry in the process table. */
static int wpr_enter(HANDLE process, HANDLE ichan, HANDLE ochan)
{
  register int wpr;

  for(wpr=0; wpr<MAXPROCS; wpr++){
    if(wprtable[wpr].hProcess == 0){
      wprtable[wpr].hProcess = process;
      wprtable[wpr].hIchan = ichan;
      wprtable[wpr].hOchan = ochan;
      return(1);
    }
  }
  return(0);
}

/* WPR_GETCHAN -- Get the codes for the IPC channels assigned to a process */
static HANDLE wpr_getchan (HANDLE process, HANDLE *ichan, HANDLE *ochan)
{
  register WPROC *wpr;

  /* Lookup process in table.  Return an error if there is no entry */
  if ((wpr = wpr_findprocess(process)) == NULL){
    return(NULL);
  }
  else{
    *ichan = wpr->hIchan;
    *ochan = wpr->hOchan;
    return (process);
  }
}

/* WPR_RELEASE -- Release the table entry for the process.  Used when a process
 * is killed and we do not wish to wait for process termination */
static void wpr_release (HANDLE process)
{
  register WPROC *wpr;

  if ((wpr = wpr_findprocess (process)) != NULL){
    wpr->hProcess = 0;
    wpr->hIchan = 0;
    wpr->hOchan = 0;
  }
}

/*
 *
 * Public Routines
 *
 *
 */

int WinProcessOpen(char *cmd, HANDLE *ichan, HANDLE *ochan, HANDLE *process)
{
  HANDLE g_hChildStd_IN_Rd = NULL;
  HANDLE g_hChildStd_IN_Wr = NULL;
  HANDLE g_hChildStd_OUT_Rd = NULL;
  HANDLE g_hChildStd_OUT_Wr = NULL;

  SECURITY_ATTRIBUTES saAttr; 
  PROCESS_INFORMATION piProcInfo; 
  STARTUPINFO siStartInfo;
  BOOL bSuccess = FALSE; 
  
  // Set the bInheritHandle flag so pipe handles are inherited. 
  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
  saAttr.bInheritHandle = TRUE; 
  saAttr.lpSecurityDescriptor = NULL; 
  
  // Create a pipe for the child process's STDOUT. 
  if ( !CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) ) 
    return 0;
  
  // Ensure the read handle to the pipe for STDOUT is not inherited.
  if ( !SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
    return 0;
  
  // Create a pipe for the child process's STDIN. 
  if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) 
    return 0;
  
  // Ensure the write handle to the pipe for STDIN is not inherited. 
  if ( !SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
    return 0;
  
  // Set up members of the PROCESS_INFORMATION structure. 
  ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
  
  // Set up members of the STARTUPINFO structure. 
  // This structure specifies the STDIN and STDOUT handles for redirection.
  ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
  siStartInfo.cb = sizeof(STARTUPINFO); 
  siStartInfo.hStdError = g_hChildStd_OUT_Wr;
  siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
  siStartInfo.hStdInput = g_hChildStd_IN_Rd;
  siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
  
  // Create the child process. 
  bSuccess = CreateProcess(NULL,
			   cmd,		  // command line 
			   NULL,          // process security attributes 
			   NULL,          // primary thread security attributes 
			   TRUE,          // handles are inherited 
			   0,             // creation flags 
			   NULL,          // use parent's environment 
			   NULL,          // use parent's current directory 
			   &siStartInfo,  // STARTUPINFO pointer 
			   &piProcInfo);  // receives PROCESS_INFORMATION 
  
  // check for errors and configure return values
  if( bSuccess ) {
    /* save process info for later */
    wpr_enter (piProcInfo.hProcess, g_hChildStd_OUT_Rd, g_hChildStd_OUT_Rd);
    // Close handles to the child process and its primary thread.
    // Some applications might keep these handles to monitor the status
    // of the child process, for example. 
    // CloseHandle(piProcInfo.hProcess);
    CloseHandle(piProcInfo.hThread);
    // close unused sides of the pipe
    CloseHandle(g_hChildStd_OUT_Wr);
    CloseHandle(g_hChildStd_IN_Rd);
    /* package up process info for return */
    *ichan = (void *)g_hChildStd_OUT_Rd;
    *ochan = (void *)g_hChildStd_IN_Wr;
    *process = (void *)piProcInfo.hProcess;
    /* success */
    return 1;
  } else {
    /* failure */
    *ichan = NULL;
    *ochan = NULL;
    *process = NULL;
    return 0;
  }
}

/* 
 *
 * WinProcessClose --  Close a connected subprocess
 *
 */
int WinProcessClose(void *vprocess, int *exit_status)
{
  HANDLE ichan, ochan;
  HANDLE process = (HANDLE)vprocess;


  *exit_status = 0;
  if( wpr_getchan(process, &ichan, &ochan) != NULL ){
    CloseHandle(ochan);
    CloseHandle(ichan);
    CloseHandle(process);
    wpr_release(process);
  }
  return(*exit_status);
}


/* 
  *
  * WinProcessRead -- Read next record from an IPC channel.
  *
  * Since UNIX pipes are byte streams we must take special measures to
  * transmit data through a pipe in records.  
  * Each block of data is preceded by a header of sizeof(int) consisting
  * containing the number of bytes in the block.
  * To read a block we must read the count and then issue successive read
  * requests until the entire block has been read.
  *
*/
void *WinProcessRead(void *vfd, void *buf, int maxbytes, int *got)
{
  register char *op;
  register int nbytes;
  char *obuf;
  int record_length;
  int temp;
  DWORD tgot;
  HANDLE fd = (HANDLE)vfd;

  /* no data read as yet */
  *got = 0;

  /* Get byte count of record.
   */
  if((ReadFile(fd, &temp, sizeof(int), &tgot, NULL) == FALSE) || 
     (tgot != sizeof(int)))
    return NULL;
  record_length = temp;
  if( maxbytes >= 0 )
    nbytes = min(record_length, maxbytes);
  else
    nbytes = record_length;

  /* allocate output buffer, if necessary */
  if( buf )
    obuf = buf;
  else{
    obuf = (char *)malloc(nbytes);
    if( !obuf ) return NULL;
  }
  op = (char *)obuf;

  /* Now read exactly nbytes of data from channel into user buffer.
   * Return actual byte count if EOF is seen.  If an error is seen, return.
   * If necessary multiple read requests are issued to read the
   * entire record.
   */
  while (nbytes > 0){
    if( ReadFile(fd, op, nbytes, &tgot, NULL) == FALSE ){
      if( !buf ) free(obuf);
      *got = 0;
      return NULL;
    } else if( tgot == 0 ){
      return(obuf);
    } else {
      nbytes -= tgot;
      *got += tgot;
      op += tgot;
    }
  }

  /* If the record is larger than maxbytes, we must read and discard
   * the additional bytes.  The method used is inefficient but it is
   * unlikely that we will be called to read less than a full record.
   */
  if( maxbytes >= 0 ){
    for (nbytes = maxbytes;  nbytes < record_length;  nbytes++){
      if( (ReadFile(fd, &temp, 1, &tgot, NULL) == FALSE) || (tgot == 0) ){
	break;
      }
    }
  }
  return(obuf);
}

/* 
 *
 * ProcessWrite -- Write to an IPC channel.
 * Write the IPC block header followed by the data block.
 *
*/
int WinProcessWrite(void *vfd, void *buf, int nbytes)
{
  DWORD tgot;
  BOOL got;
  HANDLE fd = (HANDLE)vfd;

  /* write byte count */
  WriteFile(fd, &nbytes, sizeof(int), &tgot, NULL);

  /* write data block */
  got = WriteFile(fd, buf, nbytes, &tgot, NULL);
  if( got == FALSE ){
    return(-1);
  } else {
    return((int)tgot);
  }
}

#else

int have_winprocess=0;

#endif