tiny-shell 0.2
A mini shell project aiming to gain knowledge about Win32 and Linux API
Loading...
Searching...
No Matches
operations_linux.c
Go to the documentation of this file.
1#ifdef __linux__
2# include "../core/config.h"
3# include "../core/io_wrap.h"
4# include "../core/utils.h"
5# include "operations.h"
6# include "type.h"
7
8# include <assert.h>
9# include <dirent.h>
10# include <errno.h>
11# include <libgen.h>
12# include <stdio.h>
13# include <stdlib.h>
14# include <string.h>
15# include <sys/stat.h>
16# include <sys/wait.h>
17# include <time.h>
18# include <unistd.h>
19
20extern char **environ; // NOLINT
21
22void report_error_code(int code) {
23 switch(code) {
24 case 0:
25 break;
26 case EACCES:
27 format_error("Access denied!\n");
28 break;
29 case EBADF:
30 format_error("Not a valid filename\n");
31 break;
32 case ENOTDIR:
33 format_error("Not a directory\n");
34 break;
35 case ENOENT:
36 format_error("No such file or directory\n");
37 break;
38 case EISDIR:
39 format_error("Is a directory\n");
40 break;
41 case EPERM:
42 format_error("Operation not permitted\n");
43 break;
44 case ESRCH:
45 format_error("No such process\n");
46 break;
47 default:
48 format_error("System error code: %d\n", code);
49 }
50}
51
52void get_cwd(unsigned int buffer_size, os_char *buffer) {
53 if(!getcwd(buffer, buffer_size)) {
54 format_error("Cannot get current working directory\n");
55 }
56}
57
58bool change_cwd(const os_char *new_dir) {
59 int error_code = chdir(new_dir);
60 if(error_code == 0) {
61 return true;
62 }
63 report_error_code(errno);
64 return false;
65}
66
67void clear_screen() {
68 printf("\e[1;1H\e[2J");
69}
70
71bool delete_file(const char *filename) {
72 const int status = unlink(filename);
73 if(status == 0) {
74 format_success("File removed successfully.\n");
75 return true;
76 }
77 report_error_code(errno);
78 return false;
79}
80
81bool lsdir(const char *dir) {
82 struct dirent *de = NULL;
83 DIR *dr = opendir(dir);
84 int entries_count = 0;
85
86 if(dr == NULL) {
87 goto LS_FAIL;
88 }
89 // NOLINTNEXTLINE
90 while((de = readdir(dr)) != NULL) {
91 if(de->d_type == DT_DIR)
92 format_success("%s/\n", de->d_name);
93 else if(de->d_type == DT_REG)
94 format_output("%s\n", de->d_name);
95 else
96 format_output("%s(?)\n", de->d_name);
97 entries_count++;
98 }
99 closedir(dr);
100 if(entries_count > 0)
101 return true;
102LS_FAIL:
103 format_error("Cannot get directory information\n");
104 return false;
105}
106
107// NOLINTBEGIN(misc-unused-parameters)
108void handle_background_child_exit(int sig) {
109 int stat = 0;
110 waitpid(-1, &stat, WNOHANG);
111}
112// NOLINTEND(misc-unused-parameters)
113
114bool launch_executable(const struct args args) {
115 assert(args.argc > 0 && "Invalid args");
116 struct args copied_args;
117 args_deep_copy_init(&copied_args, &args);
118 int pid = fork();
119 int stat_loc = 0;
120 if(pid == -1) {
121 report_error_code(errno);
122 args_destroy(&copied_args);
123 return false;
124 }
125 if(pid == 0) {
126 if(!args.background)
127 signal(SIGINT, SIG_DFL);
128 if(execvp(copied_args.argv[0], copied_args.argv) == -1) {
129 report_error_code(errno);
130 _exit(EXIT_FAILURE);
131 }
132 _exit(EXIT_SUCCESS);
133 } else {
134 if(!args.background) {
135 waitpid(pid, &stat_loc, 0);
136 } else {
137 signal(SIGCHLD, handle_background_child_exit);
138 goto RETURN_TRUE;
139 }
140 if(WIFEXITED(stat_loc)) {
141 const int exit_code = WEXITSTATUS(stat_loc);
142 if(exit_code != 0) {
143 format_error("Exit code: %d\n", exit_code);
144 goto RETURN_FALSE;
145 }
146 goto RETURN_TRUE;
147 } else if(WIFSTOPPED(stat_loc)) {
148 format_error("Stopped\n");
149 goto RETURN_FALSE;
150 } else if(WCOREDUMP(stat_loc)) {
151 format_error("Core dumped\n");
152 goto RETURN_FALSE;
153 } else if(WIFSIGNALED(stat_loc)) {
154 if(WTERMSIG(stat_loc) == SIGSEGV) {
155 format_error("Segmentation fault\n");
156 goto RETURN_FALSE;
157 } else {
158 format_error("Terminated by signal\n");
159 goto RETURN_FALSE;
160 }
161 } else {
162 format_error("Unknown error\n");
163 goto RETURN_FALSE;
164 }
165 RETURN_TRUE:
166 args_destroy(&copied_args);
167 return true;
168 RETURN_FALSE:
169 args_destroy(&copied_args);
170 return false;
171 }
172}
173
174bool set_shell_env(const os_char *name, const os_char *val) {
175 const int res = setenv(name, val == NULL ? "" : val, true); // NOLINT
176 if(res == 0) {
177 return true;
178 }
179 report_error_code(errno);
180 return false;
181}
182
183bool unset_shell_env(const os_char *name) {
184 const int res = unsetenv(name); // NOLINT
185 if(res == 0) {
186 return true;
187 }
188 report_error_code(errno);
189 return false;
190}
191
192bool get_shell_env(const os_char *var, unsigned int buffer_size, os_char *buffer) {
193 const os_char *res = getenv(var); // NOLINT
194 if(!res) {
195 format_error("Environment variable does not exist\n");
196 return false;
197 }
198 const unsigned int len = strlen(res);
199 if(len >= buffer_size) {
200 format_error("Insufficient buffer size\n");
201 return false;
202 }
203 memcpy(buffer, res, len);
204 buffer[len] = '\0';
205 return true;
206}
207
208bool has_shell_env(const os_char *var) {
209 return getenv(var) != NULL; // NOLINT
210}
211
212os_char *get_all_shell_env_display() {
213 os_char *res = malloc(ENVS_RESERVE_SIZE);
214 unsigned int len = 0;
215 unsigned int reserve_len = ENVS_RESERVE_SIZE;
216
217 for(int i = 0; *(environ + i) != NULL; ++i) {
218 const os_char *var = *(environ + i);
219 const unsigned int var_len = strlen(var);
220 const unsigned int new_len = len + 1 + var_len; // 1 for '\n'
221 while(new_len >= reserve_len) {
222 reserve_len += ENVS_RESERVE_SIZE;
223 os_char *new_res = realloc(res, reserve_len);
224 if(!new_res) {
225 free(res);
226 format_error("Cannot allocate enough memory to copy environment variables\n");
227 return NULL;
228 }
229 res = new_res;
230 }
231 memcpy(res + len, var, var_len);
232 len = new_len;
233 res[new_len - 1] = '\n';
234 }
235 res[len] = '\0';
236 return res;
237}
238
239bool enum_proc() {
240 DIR *procdir = NULL;
241 struct dirent *entry = NULL;
242 char path[PATH_MAX];
243
244 // Open the /proc directory
245 procdir = opendir("/proc");
246 if(procdir == NULL) {
247 format_error("Cannot open /proc");
248 report_error_code(errno);
249 return false;
250 }
251 // Loop through entries in /proc
252 // NOLINTNEXTLINE
253 while((entry = readdir(procdir)) != NULL) {
254 // Check if entry is a numeric directory (potential process ID)
255 if(!is_number(entry->d_name))
256 continue;
257 // Construct path to process status file
258 snprintf(path, sizeof(path), "/proc/%s/status", entry->d_name);
259 // Open the status file
260 FILE *statusfile = fopen(path, "r");
261 if(statusfile == NULL) {
262 continue;
263 }
264# define NAME_LENGTH 512
265 char name[NAME_LENGTH];
266 // Read the first line for process name
267 if(fscanf(statusfile, "Name: %s512\n", name) != 1) {
268 fclose(statusfile);
269 continue;
270 }
271 // Get PID using entry->d_name (assuming it's numeric)
272 pid_t pid = atoi(entry->d_name);
273 // Get parent process ID
274 char line[NAME_LENGTH];
275 pid_t ppid = -1;
276 // NOLINTBEGIN
277 while(fgets(line, sizeof(line), statusfile) != NULL) {
278 // Find the line starting with "PPid:"
279 if(strncmp(line, "PPid:", 5) == 0) {
280 sscanf(line + 5, "%d", &ppid); // Extract PPID from the line
281 break;
282 }
283 }
284 // NOLINTEND
285 // Get thread count
286 DIR *t_procdir = NULL;
287 char thread_count[3];
288 snprintf(path, sizeof path, "/proc/%d/task", pid);
289 t_procdir = opendir(path);
290
291 if(t_procdir == NULL) {
292 thread_count[0] = '?';
293 thread_count[1] = '\0';
294 } else {
295 unsigned int t_count = 0;
296 struct dirent *entry = NULL;
297 // NOLINTNEXTLINE
298 while((entry = readdir(t_procdir)) != NULL) {
299 if(!is_number(entry->d_name))
300 continue;
301 t_count++;
302 }
303 snprintf(thread_count, sizeof thread_count, "%d", t_count);
304 closedir(t_procdir);
305 }
306 // Print process information
307 printf("PID: %6u PPID: %6u T %3s Name: %s \n", pid, ppid, thread_count, name);
308 fclose(statusfile);
309 }
310 closedir(procdir);
311 return true;
312}
313
314bool minibat(const struct args args) {
315 enum { buffer_size = 300 };
316 os_char *buffer = malloc(buffer_size * sizeof(os_char));
317 if(readlink("/proc/self/exe", buffer, buffer_size) == -1) {
318 report_error_code(errno);
319 free(buffer);
320 return false;
321 }
322 // Append `"${buffer} -f "` and launch the executable
323 enum { count = 2 };
324 struct args *mod = malloc(sizeof(struct args));
325 mod->argc = count + args.argc;
327 mod->argv = malloc((mod->argc + 1) * sizeof(os_char *));
328
329 mod->argv[0] = buffer;
330
331 mod->argv[1] = malloc(3 * sizeof(os_char));
332 memcpy(mod->argv[1], "-f", 2);
333 mod->argv[1][2] = '\0';
334
335 for(int i = count; i < mod->argc; ++i) {
336 const unsigned int len = strlen(args.argv[i - count]);
337 mod->argv[i] = malloc((len + 1) * sizeof(os_char));
338 memcpy(mod->argv[i], args.argv[i - count], len);
339 mod->argv[i][len] = '\0';
340 }
341 mod->argv[mod->argc] = NULL;
342 const bool res = launch_executable(*mod);
343 args_destroy(mod);
344 free(mod);
345 return res;
346};
347
348bool check_process_exist(int proc_id) {
349 const bool exists = kill(proc_id, 0) == 0;
350 if(!exists) {
351 report_error_code(errno);
352 return false;
353 }
354 return true;
355}
356
357bool show_child_processes(int proc_id) {
358 DIR *dir = opendir("/proc");
359 if(dir == NULL) {
360 format_error("Unable to open /proc");
361 return false;
362 }
363 struct dirent *entry = NULL;
364 char path_buffer[CWD_BUFFER_SIZE];
365 FILE *fp = NULL;
366 pid_t ppid = 0;
367 int countChildProcess = 0;
368
369 // NOLINTNEXTLINE
370 while((entry = readdir(dir)) != NULL) {
371 if(entry->d_type == DT_DIR && is_number(entry->d_name)) {
372 snprintf(path_buffer, sizeof(path_buffer), "/proc/%s/stat", entry->d_name);
373 fp = fopen(path_buffer, "r");
374 if(fp == NULL) {
375 continue;
376 }
377
378 // Fields in /proc/[pid]/stat as per 'man proc'
379 int pid = 0;
380 char comm[CWD_BUFFER_SIZE];
381 char state = '\0';
382 if(fscanf(fp, "%d %s %c %d", &pid, comm, &state, &ppid) != 4) {
383 fclose(fp);
384 continue;
385 }
386 fclose(fp);
387
388 if(ppid == proc_id) {
389 // Get the thread count
390 snprintf(path_buffer, sizeof(path_buffer), "/proc/%d/status", pid);
391 fp = fopen(path_buffer, "r");
392 if(fp != NULL) {
393 char line[CWD_BUFFER_SIZE];
394 int threads = 0;
395 while(fgets(line, sizeof(line), fp)) {
396 if(sscanf(line, "Threads: %d", &threads) == 1) {
397 break;
398 }
399 }
400 fclose(fp);
401
402 // Remove parentheses around the process name
403 comm[strlen(comm) - 1] = '\0';
404 format_success("PID: %6d T: %3d Name: %s \n", pid, threads, comm + 1);
405 countChildProcess++;
406 }
407 }
408 }
409 }
410 closedir(dir);
411
412 if(countChildProcess == 0) {
413 format_success("No process with parent's PID %d\n", proc_id);
414 }
415
416 return true;
417}
418
419bool get_date() {
420 os_char buffer[TIME_DATE_BUFFER_SIZE];
421 const time_t current_time = time(NULL);
422 struct tm *local_time = localtime(&current_time); // NOLINT
423 const int initial_year = 1900;
424 format_output("The current date is: %02d/%02d/%04d.\n", local_time->tm_mday, local_time->tm_mon + 1, local_time->tm_year + initial_year);
425 return true;
426}
427
428bool get_time() {
429 os_char buffer[TIME_DATE_BUFFER_SIZE];
430 const time_t current_time = time(NULL);
431 struct tm *local_time = localtime(&current_time); // NOLINT
432 format_output("The current time is: %02d:%02d:%02d.\n", local_time->tm_hour, local_time->tm_min, local_time->tm_sec);
433 return true;
434}
435
436bool kill_process(int proc_id) {
437 if(!check_process_exist(proc_id)) {
438 return false;
439 }
440 if(kill(proc_id, SIGTERM) == 0) {
441 format_success("Process with ID %d is terminated.\n", proc_id);
442 return true;
443 }
444 report_error_code(errno);
445 format_error("Can't terminate process with id %d\n.", proc_id);
446 return false;
447}
448
449bool resume(int proc_id) {
450 if(!check_process_exist(proc_id))
451 return false;
452 if(kill(proc_id, SIGCONT) == 0) {
453 format_success("Resume running process with ID %d\n", proc_id);
454 return true;
455 }
456 report_error_code(errno);
457 format_error("Can't resume process with id %d\n.", proc_id);
458 return false;
459}
460
461bool stop_proccess(int proc_id) {
462 if(!check_process_exist(proc_id))
463 return false;
464 if(kill(proc_id, SIGSTOP) == 0) {
465 format_success("Stopped running process with ID %d\n", proc_id);
466 return true;
467 }
468 report_error_code(errno);
469 format_error("Can't stop process with id %d\n.", proc_id);
470 return false;
471}
472
473#endif
void args_deep_copy_init(struct args *obj, const struct args *source)
Initialize the object by deep copying source
Definition args.c:163
void args_destroy(struct args *obj)
Definition args.c:135
#define TIME_DATE_BUFFER_SIZE
Definition config.h:11
#define CWD_BUFFER_SIZE
Definition config.h:6
#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
bool is_number(const char *c)
Definition utils.c:8