-
Notifications
You must be signed in to change notification settings - Fork 0
/
env_parsing.c
186 lines (176 loc) · 7.14 KB
/
env_parsing.c
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
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* env_parsing.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: rjobert <rjobert@student.42barcelo> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/07/25 18:01:16 by rjobert #+# #+# */
/* Updated: 2023/08/01 20:03:09 by rjobert ### ########.fr */
/* */
/* ************************************************************************** */
#include "pipex.h"
/*
Extract the PATH from environment variables in env[]
1 - we receive env from the command line, it is array of char*
if it is null (env empty), we return a matrix split of a default path
2 - else we itetrate over each env[x] while not null, iterate while first
four chars are different than "PATH"
3 - when we found it, we return it minus the 5 first char (that are "PATH=")
4 - if there an is no path in the env (extremely rare but this is 42 school...)
we replace it by a pre-define PATH in the header
*/
char **search_path(char *env[])
{
int i;
char **path_matrix;
char *path;
path = NULL;
i = 0;
if (!env || !*env)
{
path_matrix = ft_split(DEF_PATH, ':');
return (path_matrix);
}
else
{
while (env[i] && ft_strncmp(env[i], "PATH", 4))
i++;
if (env[i])
path = env[i] + 5;
else
path = DEF_PATH;
}
path_matrix = ft_split(path, ':');
return (path_matrix);
}
/* now let's look for a command line function in the path and
return the path exec
1 - we iterate over all path (if there are some) and
use strjoin to to first go from cmd (like cat) to a
cmd path (like /cat) and use strjoin again on a potential path
2 - we free free the path_cmd and we now use the access function
to check if path/cmd (like /bin/cat) exist with F_OK.
3 - if it does, we return (and free in another function) else
we free and move to next potential from in all_path */
char *exec_path(char **all_path, char *cmd)
{
char *test_path;
char *tmp_cmd;
if (!all_path)
return (NULL);
while (*all_path)
{
tmp_cmd = ft_strjoin("/", cmd);
test_path = ft_strjoin(*all_path, tmp_cmd);
free(tmp_cmd);
if (access(test_path, F_OK) == 0)
return (test_path);
free(test_path);
all_path++;
}
return (NULL);
}
/*
objective : we build the execve with error check along the way
inputs for execve are path (/bin/cat), an array of command info
(ex : "ls -l" would yield {"ls, "-l", NULL}) and the env variables.
1- build command array : from cmd (argv) to arg_cmd ({cmd, arguments, NULL}):
we split the the cmd argument with ft_split on space "ls -l" from user input
in argv[] becomes: arg_cmd = ["ls", "-l", NULL]
1.b - special case for one command : awk and sed it is entered as
awk 'set of actions' so we apply special_split() focus around "" or ''
2 - we use search_path to look for the path inside the env[] and return a
splitmatrix of all path separated by ':' as in LINUX environment
3 - we search for the correct path inside the matrix with exec_path : if access
function return a path, it is where the cmd can be found,
otherwise return null. we will need to free afterward as we used malloc
3.b - special case when the cmd is with absolute path
(instead of cat user entered /bin/cat) in that case the path is already here,
so if arg_user[0] = '/', path = arg_user (i.e. arg_cmd[0])
3.c - special case if we have relative path (like ./script.sh) in that case
the execve function needs arguments to be clean for special cases so we
apply the function relative_path_clean()
4 - we can clean our path matrix, and free the cmplete_path after using
execve_bash : an application of execve with special error handling
*/
void build_exec(char *arg_user, char *env[], int fd)
{
char **unix_paths;
t_cmd *path_cmd;
if (!arg_user)
return ;
path_cmd = malloc(sizeof(t_cmd));
if (!path_cmd)
return ;
path_cmd->fd = fd;
if (!ft_strncmp(arg_user, "awk", 3) || !ft_strncmp(arg_user, "sed", 3))
path_cmd->args = special_split(arg_user, find_sep(arg_user));
else
path_cmd->args = special_split(arg_user, ' ');
unix_paths = search_path(env);
if (arg_user[0] == '/' || (arg_user[0] == '.' && arg_user[1] == '/'))
fill_path(&path_cmd, path_cmd->args[0], 0);
else
fill_path(&path_cmd, exec_path(unix_paths, path_cmd->args[0]), 1);
if (arg_user[0] == '.' && arg_user[1] == '/')
relative_path_clean(&arg_user, &path_cmd);
free_split(&unix_paths);
execve_bash(&path_cmd, env);
}
/*
Objective : apply execve with many error handling
1 - first if the path is findable (the path reach a file that exist -
we verify that (again) with access and F_OK) we then test several cases:
1.a - if the file is not exectutable ( a script.sh where -x- right are
not for user or group -> we return error 126 and exit message)
1.b - else we can apply execve and return the error message if issue
2 - if the path is not ok but arg_cmd[0] contains a "/" it is possible
that our command is a relative path with directory like subdir/scripth.sh
and in that case iterating or env wasn't a clear sucees, so we apply execve
with arg_cmd[0] as path
3 - if nothing worked, command is clearly unknown -> exit error message
*/
void execve_bash(t_cmd **p_cmd, char **env)
{
if (access((*p_cmd)->path, F_OK) == 0)
{
if (access((*p_cmd)->path, X_OK) != 0)
exit_error(126, strerror(errno), (*p_cmd)->args[0], p_cmd);
if (execve((*p_cmd)->path, (*p_cmd)->args, env) == -1)
exit_error(errno, strerror(errno), (*p_cmd)->args[0], p_cmd);
}
if (ft_strchr((*p_cmd)->args[0], '/')
&& access((*p_cmd)->args[0], F_OK) == 0)
{
if (execve((*p_cmd)->args[0], (*p_cmd)->args, env) == -1)
exit_error(errno, strerror(errno), (*p_cmd)->args[0], p_cmd);
}
exit_error(127, "command not found", (*p_cmd)->args[0], p_cmd);
}
/*
the issue is that if argument is "awk '{action} pattern2 {action2}'"
or same usage for sed, ft_split will break our argv[] in too many parts.
Indeed if we do a split on " " (space) we have :
arg_cmd[1] : '{action}
arg_cmd[2] : pattern2
arg_cmd[3] : {action2}'
but we need to rebuild it as execve needs to be
execve(/correctpath/awk, args, env) with args as :
char *args[] = {"awk", "{count++} END {print count}", "input.tx
so we do :
1 - a split using special_split() on user arguments but this time on
the type of "" or '' entered by the user, so first iterate to find the
first ' or " in the command. We then split on this separator
2 - the only issue is that in the arguments resulting from the split, the
arguments[0] will be "awk " and we want to get rid of this space using
the strdup custom (we copy only intil space and free the initial word we
allocated memory for the original "awk " inside the split function)
*/
char **special_split(char *arg_user, char c)
{
char **arguments;
arguments = split_quotes(arg_user, c);
arguments[0] = strdup_custom(arguments[0], ' ');
return (arguments);
}