1 
2 #ifdef __INTEL_COMPILER
3 #define _BSD_SOURCE 1
4 #define _POSIX_C_SOURCE  200809L
5 #endif
6 
7 #include <stdio.h>
8 #include <stdbool.h>
9 #include "windows.h"
10 #include "utils.h"
11 
12 #define MIN(a,b) ((a<b)?a:b)
13 #define MAX(a,b) ((a>b)?a:b)
14 
15 
getppid()16 int getppid() {
17     int pid = GetCurrentProcessId();
18 
19     HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
20     PROCESSENTRY32 pe;
21     // Set the size of the structure before using it.
22     pe.dwSize = sizeof(PROCESSENTRY32);
23 
24     // Get info about first process.
25     if(!Process32First(hProcessSnap, &pe)) {
26         printf("Unable to get parent pid");
27         exit(1);
28     }
29 
30     // Walk the snapshot of processes to find the parent.
31     do {
32         if (pe.th32ProcessID == pid) {
33             return pe.th32ParentProcessID;
34         }
35     } while(Process32Next(hProcessSnap, &pe));
36 
37     CloseHandle(hProcessSnap);
38     printf("Unable to get parent pid");
39     exit(1);
40 }
41 
42 
open_stdin()43 HANDLE open_stdin() {
44     HANDLE h_input = GetStdHandle(STD_INPUT_HANDLE);
45 
46     if (h_input == INVALID_HANDLE_VALUE) {
47         printf("Unable to get stdin handle.");
48         exit(1);
49     }
50 
51     return h_input;
52 }
53 
54 
open_named_pipe(const char * pipe_name)55 HANDLE open_named_pipe(const char* pipe_name) {
56     HANDLE h_input = CreateFile(pipe_name,
57         GENERIC_READ,
58         0,
59         NULL,
60         OPEN_EXISTING,
61         FILE_ATTRIBUTE_NORMAL,
62         NULL
63     );
64 
65     if (h_input == INVALID_HANDLE_VALUE) {
66         printf("CreateFile failed with error %u\n", (unsigned)GetLastError());
67         exit(1);
68     }
69 
70     return h_input;
71 }
72 
73 
configure_input_handle(HANDLE h_input)74 void configure_input_handle(HANDLE h_input) {
75     DWORD handle_type = GetFileType(h_input);
76 
77     if (handle_type == FILE_TYPE_CHAR) {
78 
79         DWORD lpmode;
80         GetConsoleMode(h_input, &lpmode);
81 
82         // Disable line input
83         lpmode = lpmode &
84                  ~ENABLE_LINE_INPUT &
85                  ~ENABLE_ECHO_INPUT;
86 
87         // Only listen for character input events
88         if (!SetConsoleMode(h_input, lpmode)) {
89             printf("Unable to set console mode. %d", (int)GetLastError());
90             exit(1);
91         }
92 
93     } else if (handle_type == FILE_TYPE_PIPE) {
94         // No need to do anything
95     } else if (handle_type == FILE_TYPE_DISK) {
96         printf("Don't know how to handle FILE_TYPE_DISK.");
97         exit(1);
98     } else {
99         printf("Unknown input type.");
100         exit(1);
101     }
102 }
103 
104 
105 // If there's a complete line of text, put that line in buffer, and return the
106 // number of characters. Otherwise, return NULL.
get_line_nonblock(char * buf,int max_chars,HANDLE h_input)107 char* get_line_nonblock(char* buf, int max_chars, HANDLE h_input) {
108     // Check what type of thing we're reading from
109     DWORD input_type = GetFileType(h_input);
110 
111     // Debugging info
112     char* input_type_name;
113     switch(input_type) {
114         case FILE_TYPE_CHAR:
115             input_type_name = "FILE_TYPE_CHAR (console)";
116             break;
117         case FILE_TYPE_DISK:
118             input_type_name = "FILE_TYPE_DISK";
119             break;
120         case FILE_TYPE_PIPE:
121             input_type_name = "FILE_TYPE_PIPE";
122             break;
123         default:
124             input_type_name = "Unknown";
125     }
126 
127 
128     if (input_type == FILE_TYPE_CHAR) {
129         // Attempt to read enough to fill the buffer
130         DWORD num_peeked;
131         INPUT_RECORD in_record_buf[WIN_INPUT_BUF_LEN];
132         char input_char_buf[WIN_INPUT_BUF_LEN];
133         int input_char_buf_n = 0;
134 
135         // First use PeekConsoleInput to make sure some char is available,
136         // because ReadConsoleInput will block if there's no input.
137         if (!PeekConsoleInput(h_input, in_record_buf, WIN_INPUT_BUF_LEN, &num_peeked)) {
138             printf("Error peeking at console input.\n");
139             return NULL;
140         };
141 
142         if (num_peeked == 0) {
143             return NULL;
144         }
145 
146         bool found_newline = false;
147 
148         int i;
149         for (i=0; i<num_peeked; i++) {
150             // We're looking for key down events where the value is not 0.
151             // (Special keys like Shift will have AsciiChar value of 0.)
152             if (in_record_buf[i].EventType == KEY_EVENT &&
153                 in_record_buf[i].Event.KeyEvent.bKeyDown &&
154                 in_record_buf[i].Event.KeyEvent.uChar.AsciiChar != 0)
155             {
156                 // Store the character in input_char_buf. If there's a \n, then
157                 // copy in_record_buf (up to the \n) to buf.
158                 char c = in_record_buf[i].Event.KeyEvent.uChar.AsciiChar;
159 
160                 if (c == '\r') {
161                     found_newline = true;
162                     input_char_buf[input_char_buf_n] = '\n';
163                     input_char_buf_n++;
164                     break;
165                 } else {
166                     input_char_buf[input_char_buf_n] = c;
167                     input_char_buf_n++;
168                 }
169             }
170         }
171 
172 
173         if (found_newline) {
174             // This is the number of events up to and including the '\n'
175             DWORD num_events_read = i+1;
176             DWORD num_events_read2;
177             // Clear out console buffer up to the '\n' event
178             if (!ReadConsoleInput(h_input, in_record_buf, num_events_read , &num_events_read2)) {
179                 printf("Error reading console input.\n");
180                 return NULL;
181             }
182 
183             // Place the content in buf
184             snprintf(buf, MIN(input_char_buf_n, max_chars), "%s", input_char_buf);
185             return buf;
186 
187         } else {
188             return NULL;
189         }
190 
191     } else if (input_type == FILE_TYPE_PIPE) {
192         DWORD num_peeked;
193         char input_char_buf[WIN_INPUT_BUF_LEN];
194         int input_char_buf_n = 0;
195 
196         if (!PeekNamedPipe(h_input, input_char_buf, WIN_INPUT_BUF_LEN, &num_peeked, NULL, NULL)) {
197             printf("Error peeking at pipe input. Error %d.\n", (unsigned)GetLastError());
198             return NULL;
199         };
200 
201         bool found_newline = false;
202         for (int i=0; i<num_peeked; i++) {
203             if (input_char_buf[i] == '\r' || input_char_buf[i] == '\n') {
204                 found_newline = true;
205             }
206             input_char_buf_n++;
207         }
208 
209         DWORD num_read;
210         if (found_newline) {
211             // Clear out pipe
212             if (!ReadFile(h_input, input_char_buf, input_char_buf_n, &num_read, NULL)) {
213                 printf("Error reading pipe input.\n");
214                 return NULL;
215             }
216 
217             // Place the content in buf
218             snprintf(buf, MIN(input_char_buf_n, max_chars), "%s", input_char_buf);
219             return buf;
220 
221         } else {
222             return NULL;
223         }
224 
225     } else {
226         printf("Unsupported input type: %s\n", input_type_name);
227         exit(1);
228     }
229 
230     return buf;
231 }
232 
233 
234 // Send Ctrl-C to a program if it has a console.
sendCtrlC(int pid)235 void sendCtrlC(int pid) {
236     verbose_printf("Sending ctrl+c to pid %d", pid);
237     FreeConsole();
238 
239     if (AttachConsole(pid)) {
240         GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
241     } else {
242         verbose_printf("Error attaching to console for PID: %d\n", pid);
243     }
244 }
245 
246 // Callback function that closes a window if the PID matches the value passed
247 // in to lParam.
enumCloseWindowProc(_In_ HWND hwnd,LPARAM lParam)248 BOOL CALLBACK enumCloseWindowProc(_In_ HWND hwnd, LPARAM lParam) {
249     DWORD current_pid = 0;
250 
251     GetWindowThreadProcessId(hwnd, &current_pid);
252 
253     if (current_pid == (DWORD) lParam) {
254         PostMessage(hwnd, WM_CLOSE, 0, 0);
255     }
256 
257     return true;
258 }
259 
sendWmClose(int pid)260 void sendWmClose(int pid) {
261     EnumWindows(enumCloseWindowProc, (LPARAM)pid);
262 }
263 
264 // Terminate process by pid.
kill_pid(DWORD dwProcessId)265 BOOL kill_pid(DWORD dwProcessId) {
266     HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
267 
268     if (hProcess == NULL)
269         return FALSE;
270 
271     BOOL result = TerminateProcess(hProcess, 1);
272 
273     CloseHandle(hProcess);
274 
275     return result;
276 }
277