tiny-shell 0.2
A mini shell project aiming to gain knowledge about Win32 and Linux API
Loading...
Searching...
No Matches
operations_win.c
Go to the documentation of this file.
1#ifdef _WIN32
2
3# include "../core/config.h"
4# include "../core/io_wrap.h"
5# include "operations.h"
6
7# include <WinBase.h>
8# include <assert.h>
9# include <signal.h>
10# include <stdio.h>
11# include <string.h>
12# include <tlhelp32.h>
13# include <wchar.h>
14
15void report_error_code(DWORD err);
16
17void get_cwd(unsigned int buffer_size, os_char *buffer) {
18 DWORD status = GetCurrentDirectory((DWORD)buffer_size, (LPSTR)buffer);
19 if(status == 0 || status > buffer_size) {
20 report_error_code(GetLastError());
21 }
22}
23
24bool change_cwd(const os_char *new_dir) {
25 if(!SetCurrentDirectory(new_dir)) {
26 report_error_code(GetLastError());
27 return false;
28 }
29 return true;
30}
31
32void report_error_code(DWORD error) {
33 switch(error) {
34 case ERROR_FILE_NOT_FOUND:
35 format_error("File or directory not found\n");
36 break;
37 case ERROR_INVALID_NAME:
38 format_error("Filename, directory name, or volume label syntax is incorrect\n");
39 break;
40 case ERROR_DIRECTORY:
41 format_error("Directory name is invalid\n");
42 break;
43 case ERROR_PATH_NOT_FOUND:
44 format_error("Cannot find the path specified\n");
45 break;
46 case ERROR_ENVVAR_NOT_FOUND:
47 format_error("Environment variable not found\n");
48 break;
49 case ERROR_ACCESS_DENIED:
50 format_error("Access is denied\n");
51 break;
52 default:
53 format_error("System error code: %d\n", error);
54 }
55}
56
57void clear_screen() {
58 // From: https://learn.microsoft.com/en-us/windows/console/clearing-the-screen#example-2
59 // NOLINTBEGIN
60 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
61 CONSOLE_SCREEN_BUFFER_INFO csbi;
62 SMALL_RECT scrollRect;
63 COORD scrollTarget;
64 CHAR_INFO fill;
65
66 // Get the number of character cells in the current buffer.
67 if(!GetConsoleScreenBufferInfo(hConsole, &csbi)) {
68 return;
69 }
70
71 // Scroll the rectangle of the entire buffer.
72 scrollRect.Left = 0;
73 scrollRect.Top = 0;
74 scrollRect.Right = csbi.dwSize.X;
75 scrollRect.Bottom = csbi.dwSize.Y;
76
77 // Scroll it upwards off the top of the buffer with a magnitude of the entire height.
78 scrollTarget.X = 0;
79 scrollTarget.Y = (SHORT)(0 - csbi.dwSize.Y);
80
81 // Fill with empty spaces with the buffer's default text attribute.
82 fill.Char.UnicodeChar = TEXT(' ');
83 fill.Attributes = csbi.wAttributes;
84
85 // Do the scroll
86 ScrollConsoleScreenBuffer(hConsole, &scrollRect, NULL, scrollTarget, &fill);
87
88 // Move the cursor to the top left corner too.
89 csbi.dwCursorPosition.X = 0;
90 csbi.dwCursorPosition.Y = 0;
91
92 SetConsoleCursorPosition(hConsole, csbi.dwCursorPosition);
93 // NOLINTEND
94}
95
96bool is_empty_str(os_char *str) {
97 return !strcmp(str, "");
98}
99
100void extract_from_args(const struct args args, os_char **p_command_line) {
101 size_t len = 0;
102 if(!is_empty_str(args.argv[0])) {
103 len += strlen(args.argv[0]) + 2;
104 }
105 for(int i = 1; i < args.argc; ++i) {
106 if(is_empty_str(args.argv[i])) {
107 continue;
108 } else {
109 len += strlen(args.argv[i]) + 3;
110 }
111 }
112
113 os_char *command_line = malloc((len + 1) * sizeof(os_char));
114 len = 0;
115 if(!is_empty_str(args.argv[0])) {
116 sprintf(command_line, "\"%s\"", args.argv[0]);
117 len += strlen(args.argv[0]) + 2;
118 }
119 for(int i = 1; i < args.argc; ++i) {
120 if(is_empty_str(args.argv[i])) {
121 continue;
122 } else {
123 sprintf(command_line + len, " \"%s\"", args.argv[i]);
124 len += strlen(args.argv[i]) + 3;
125 }
126 }
127
128 command_line[len] = '\0';
129
130 *p_command_line = command_line;
131}
132
133bool kill_process(int proc_id) {
134 HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, false, proc_id);
135
136 if(hProcess == NULL) {
137 CloseHandle(hProcess);
138 format_error("Invalid process ID.\n");
139 return false;
140 } else if(TerminateProcess(hProcess, 0)) {
141 CloseHandle(hProcess);
142 format_success("Process with ID %d is terminated.\n", proc_id);
143 return true;
144 } else {
145 CloseHandle(hProcess);
146 format_error("Can't terminate process with id %d\n.", proc_id);
147 return false;
148 }
149}
150
151bool resume(int proc_id) {
152 int flag = 0;
153
154 HANDLE threadsSnapshot = INVALID_HANDLE_VALUE;
155 threadsSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
156
157 if(threadsSnapshot == INVALID_HANDLE_VALUE) {
158 format_error("Invalid process ID.\n");
159 return false;
160 }
161
162 THREADENTRY32 threadEntry;
163 threadEntry.dwSize = sizeof(THREADENTRY32);
164 Thread32First(threadsSnapshot, &threadEntry);
165 do {
166 if(threadEntry.th32OwnerProcessID == proc_id) {
167 HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE,
168 threadEntry.th32ThreadID);
169 ResumeThread(hThread);
170 CloseHandle(hThread);
171 flag = 1;
172 }
173 } while(Thread32Next(threadsSnapshot, &threadEntry));
174
175 if(flag) {
176 format_success("Resume running process with ID %d\n", proc_id);
177 return true;
178 }
179 format_error("Can't find process with ID %d\n", proc_id);
180 return false;
181}
182
183bool show_child_processes(int proc_id) {
184 HANDLE hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
185 if(hProcess == INVALID_HANDLE_VALUE) {
186 format_error("Invalid process ID\n");
187 return false;
188 }
189
190 PROCESSENTRY32 pe;
191 pe.dwSize = sizeof(PROCESSENTRY32);
192 Process32First(hProcess, &pe);
193
194 int countChildProcess = 0;
195 do {
196 if(pe.th32ParentProcessID == proc_id) {
197 format_success("PID: %6u T: %3u Name: %s \n", pe.th32ProcessID,
198 pe.cntThreads, pe.szExeFile);
199 countChildProcess++;
200 }
201 } while(Process32Next(hProcess, &pe));
202
203 CloseHandle(hProcess);
204 if(!countChildProcess) {
205 format_success("No process with parent's PID %d\n", proc_id);
206 }
207 return true;
208}
209
210bool delete_file(const os_char *filename) {
211 if(DeleteFile(filename)) {
212 format_success("File removed successfully\n");
213 return true;
214 }
215 report_error_code(GetLastError());
216 return false;
217}
218
219bool lsdir(const os_char *dir) {
220 WIN32_FIND_DATA data;
221 LARGE_INTEGER fileSize;
222 int countFile = 0;
223
224 unsigned int dir_len = strlen(dir);
225 os_char *combined = (os_char *)malloc((dir_len + 3) * sizeof(os_char));
226
227 memcpy(combined, dir, dir_len);
228 memcpy(combined + dir_len, "/*", 2);
229 combined[dir_len + 2] = '\0';
230
231 HANDLE hFind = FindFirstFile(combined, &data);
232
233 if(hFind != INVALID_HANDLE_VALUE) {
234 do {
235 if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
236 format_success("%s/\n", data.cFileName);
237 } else {
238 format_output("%s\n", data.cFileName);
239 }
240 countFile++;
241 } while(FindNextFile(hFind, &data) != 0);
242 }
243
244 FindClose(hFind);
245 free(combined);
246 if(countFile) return true;
247 format_error("Cannot get directory information\n");
248 return false;
249}
250
251bool launch_executable(const struct args args) {
252 if(!args.background) {
253 signal(SIGINT, SIG_IGN);
254 }
255
256 os_char *command_line = NULL;
257
258 extract_from_args(args, &command_line);
259
260 STARTUPINFO si;
261 PROCESS_INFORMATION pi;
262
263 ZeroMemory(&si, sizeof(si));
264 si.cb = sizeof(si);
265 ZeroMemory(&pi, sizeof(pi));
266
267 const unsigned int len = strlen(command_line);
268 os_char *tmp_command_line = malloc((len + 1) * sizeof(os_char));
269 memcpy(tmp_command_line, command_line, len * sizeof(os_char));
270 tmp_command_line[len] = '\0';
271
272 free(command_line);
273
274 if(!CreateProcess(
275 NULL, // No module name (use command line)
276 tmp_command_line, // Command line
277 NULL, // Process handle not inheritable
278 NULL, // Thread handle not inheritable
279 FALSE, // Set handle inheritance to FALSE
280 args.background ? CREATE_NEW_PROCESS_GROUP : 0, // No creation flags
281 NULL, // Use parent's environment block
282 NULL, // Use parent's starting directory
283 &si, // Pointer to STARTUPINFO structure
284 &pi // Pointer to PROCESS_INFORMATION structure
285 )) {
286 free(tmp_command_line);
287 report_error_code(GetLastError());
288 return false;
289 }
290
291 free(tmp_command_line);
292
293 // Wait until child process exits.
294 if(!args.background) {
295 WaitForSingleObject(pi.hProcess, INFINITE);
296 DWORD exit_code = 0;
297# define exit_cleanup(res) \
298 { \
299 CloseHandle(pi.hProcess); \
300 CloseHandle(pi.hThread); \
301 return res; \
302 }
303 if(GetExitCodeProcess(pi.hProcess, &exit_code) == 0) {
304 report_error_code(GetLastError());
305 exit_cleanup(false);
306 } else {
307 if(exit_code == 0) {
308 exit_cleanup(true);
309 } else {
310 format_error("Exit code: %ld\n", exit_code);
311 exit_cleanup(false);
312 }
313 }
314 }
315
316 // Close process and thread handles.
317 CloseHandle(pi.hProcess);
318 CloseHandle(pi.hThread);
319
320 exit_cleanup(true);
321# undef exit_cleanup
322}
323
324bool set_shell_env(const os_char *name, const os_char *val) {
325 if(SetEnvironmentVariable(name, val == NULL ? "" : val)) {
326 return true;
327 }
328 report_error_code(GetLastError());
329 return false;
330}
331
332bool unset_shell_env(const os_char *name) {
333 if(SetEnvironmentVariableA(name, NULL)) {
334 return true;
335 }
336 report_error_code(GetLastError());
337 return false;
338}
339
340bool get_shell_env(const os_char *var, unsigned int buffer_size, os_char *buffer) {
341 DWORD res = GetEnvironmentVariable(var, buffer, buffer_size);
342 DWORD last_err = GetLastError();
343 if(res == 0 && last_err != ERROR_SUCCESS) {
344 report_error_code(last_err);
345 return false;
346 } else if(res >= buffer_size) {
347 format_error("Insufficient buffer size\n");
348 return false;
349 }
350 return true;
351}
352
353bool has_shell_env(const os_char *var) {
354 os_char buffer[PATH_MAX];
355 DWORD res = GetEnvironmentVariable(var, buffer, PATH_MAX);
356 DWORD last_err = GetLastError();
357 if((res == 0 && last_err != ERROR_SUCCESS) || res >= PATH_MAX) {
358 return false;
359 }
360 return true;
361}
362
363os_char *get_all_shell_env_display() {
364 os_char *res = malloc(ENVS_RESERVE_SIZE);
365 unsigned int res_len = 0;
366 unsigned int reserve_len = ENVS_RESERVE_SIZE;
367 os_char *ptr = GetEnvironmentStrings();
368 for(os_char *i = ptr; *i != '\0';) {
369 const unsigned int len = strlen(i);
370 const unsigned int res_new_length = res_len + len + 1;
371 // Allocate more memory if needed
372 while(res_new_length >= reserve_len) {
373 reserve_len += ENVS_RESERVE_SIZE;
374 os_char *tmp = realloc(res, reserve_len);
375 if(!tmp) {
376 format_error("Cannot allocate enough memory\n");
377 free(res);
378 FreeEnvironmentStrings(ptr);
379 return NULL;
380 }
381 res = tmp;
382 }
383 memcpy(res + res_len, i, len);
384 res_len = res_new_length;
385 *(res + res_len - 1) = '\n';
386 i += len + 1;
387 }
388 FreeEnvironmentStrings(ptr);
389
390 *(res + res_len) = '\0';
391 return res;
392}
393
394bool minibat(const struct args args) {
395 const os_char *file = args.argv[0];
396
397 SECURITY_ATTRIBUTES sa;
398 sa.nLength = sizeof(sa);
399 sa.lpSecurityDescriptor = NULL;
400 sa.bInheritHandle = TRUE;
401
402 HANDLE hFIle = CreateFile(
403 file,
404 GENERIC_READ,
405 FILE_SHARE_READ,
406 &sa,
407 OPEN_EXISTING,
408 FILE_ATTRIBUTE_NORMAL,
409 NULL);
410
411 if(hFIle == INVALID_HANDLE_VALUE) {
412 format_error("Cannot open file %s", file);
413 return false;
414 }
415
416 PROCESS_INFORMATION pi;
417 STARTUPINFO si;
418 BOOL ret = 0;
419
420 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
421 ZeroMemory(&si, sizeof(STARTUPINFO));
422
423 si.cb = sizeof(STARTUPINFO);
424 si.dwFlags |= STARTF_USESTDHANDLES;
425 si.hStdInput = hFIle;
426 si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
427 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
428
429 ret = CreateProcess(
430 NULL,
431 "tiny-shell.exe -s",
432 NULL,
433 NULL,
434 TRUE,
435 0,
436 NULL,
437 NULL,
438 &si,
439 &pi);
440
441 if(!ret) {
442 format_error("Cannot create process %s, exit code: %d!\n", file, GetLastError());
443 CloseHandle(hFIle);
444 return false;
445 }
446
447 if(!args.background) {
448 WaitForSingleObject(pi.hProcess, INFINITE);
449 }
450
451 CloseHandle(pi.hProcess);
452 CloseHandle(pi.hThread);
453
454 CloseHandle(hFIle);
455
456 return true;
457}
458
459bool enum_proc() {
460 // NOLINTBEGIN
461 HANDLE hSnapshot = INVALID_HANDLE_VALUE;
462 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
463 if(hSnapshot == INVALID_HANDLE_VALUE)
464 return false;
465
466 PROCESSENTRY32 pe;
467 pe.dwSize = sizeof(PROCESSENTRY32);
468 Process32First(hSnapshot, &pe);
469 do {
470 printf("PID: %6lu PPID: %6lu T: %3lu Name: %s \n", pe.th32ProcessID, pe.th32ParentProcessID, pe.cntThreads, pe.szExeFile);
471 } while(Process32Next(hSnapshot, &pe));
472
473 CloseHandle(hSnapshot);
474 // NOLINTEND
475 return true;
476}
477
478bool get_time() {
479 SYSTEMTIME st = {0};
480 GetLocalTime(&st);
481
482 format_output("The current time is: %02d:%02d:%02d.\n", st.wHour, st.wMinute, st.wSecond);
483 return true;
484}
485
486bool get_date() {
487 SYSTEMTIME st = {0};
488 GetLocalTime(&st);
489
490 format_output("The current date is: %02d/%02d/%04d.\n", st.wDay, st.wMonth, st.wYear);
491 return true;
492}
493
494bool stop_proccess(int proc_id) {
495 int flag = 0;
496
497 HANDLE threadsSnapshot = INVALID_HANDLE_VALUE;
498 threadsSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
499 if(threadsSnapshot == INVALID_HANDLE_VALUE) {
500 format_error("Invalid handle value.\n");
501 return false;
502 }
503 THREADENTRY32 threadEntry;
504 threadEntry.dwSize = sizeof(THREADENTRY32);
505 Thread32First(threadsSnapshot, &threadEntry);
506
507 do {
508 if(threadEntry.th32OwnerProcessID == proc_id) {
509 HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, threadEntry.th32ThreadID);
510 SuspendThread(hThread);
511 CloseHandle(hThread);
512 flag = 1;
513 }
514 } while(Thread32Next(threadsSnapshot, &threadEntry));
515
516 if(flag) {
517 format_success("Stopped running process with ID %d\n", proc_id);
518 return true;
519 }
520 format_error("Can't find process with ID %d\n", proc_id);
521 return false;
522}
523
524#endif
#define ENVS_RESERVE_SIZE
Definition config.h:7
#define PATH_MAX
Definition config.h:10
void format_output(char *fmt,...)
Used format_xxx instead of printf and such for uniform output.
Definition io_wrap.c:80
void format_success(char *fmt,...)
Used format_xxx instead of printf and such for uniform output.
Definition io_wrap.c:69
void format_error(char *fmt,...)
Used format_xxx instead of printf and such for uniform output.
Definition io_wrap.c:89
bool stop_proccess(int proc_id)
Stop a process use it ID.
bool get_date()
Get current date.
bool has_shell_env(const os_char *var)
Check if a variable environment exists.
bool change_cwd(const os_char *new_dir)
Attempt to change current working directory.
bool get_shell_env(const os_char *var, unsigned int buffer_size, os_char *buffer)
Get value of an an environment variable.
void clear_screen()
Clear the console.
bool kill_process(int proc_id)
Kill a process.
bool show_child_processes(int proc_id)
Show child processes.
bool enum_proc()
List all running processes.
bool lsdir(const os_char *dir)
List of files or folders in specific directory.
bool minibat(const struct args args)
Launch shell executable and execute the file specified by args.argv[0]
os_char * get_all_shell_env_display()
Get all current environment variable.
bool delete_file(const os_char *filename)
Delete a specific file.
bool set_shell_env(const os_char *name, const os_char *val)
Set environment variable of the shell process.
bool launch_executable(const struct args args)
Launch the executable.
bool get_time()
Get current time.
bool unset_shell_env(const os_char *name)
Unset environment variable of the shell process.
void get_cwd(unsigned int buffer_size, os_char *buffer)
Fetch current working directory and copy them into buffer
bool resume(int proc_id)
Resume running a process.
Result after parsing an arbitrary string.
Definition args.h:10
os_char ** argv
Definition args.h:11
unsigned int argc
Definition args.h:12
bool background
Definition args.h:13