-
Notifications
You must be signed in to change notification settings - Fork 0
/
syswiz.c
319 lines (263 loc) · 8.78 KB
/
syswiz.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
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
318
319
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_PATH_LEN 2048
#define MAX_BUF_LEN 8192
struct options {
int verbose;
int file_set;
int path_set;
int inode;
char file[MAX_PATH_LEN];
char path[MAX_PATH_LEN];
};
struct output {
char buffer[MAX_BUF_LEN];
int buf_len;
};
// Fills options structure. Returns zero on error
int parse_options(struct options *opt, int argc, char **argv);
// Displays and frees output struct
void handle_output(struct options *opt, struct output *out);
// Verifies existence of path. Adjusts path buffer from relative location
// to absolute path if needed. Returns zero on error.
int verify_path(struct options * opt, char *path);
// Prepares stat struct for output
struct output *format_stat(struct options *opt, struct stat *st);
struct output *find_files(struct options *opt, char *path);
int main(int argc, char **argv)
{
// Place command-line arguments into options structure
struct options opt;
if (parse_options(&opt, argc, argv)) {
return 1;
}
if (opt.file_set) {
struct stat st;
if (stat(opt.file, &st) == -1) {
fprintf(stderr, "could not stat file: %s\n", opt.file);
return 1;
}
// Remember file inode
opt.inode = st.st_ino;
struct output *out;
if (opt.path_set) {
out = find_files(&opt, opt.path);
} else {
// Default to cwd for search path
char *cwd = getcwd(NULL, 0);
out = find_files(&opt, cwd);
free(cwd);
}
// Output all known hard-linked versions of file
handle_output(&opt, out);
// Output stat(file)
handle_output(&opt, format_stat(&opt, &st));
// TODO: don't forget to detect when there are hard-linked version that are
// unknown in location
}
return 0;
}
const char usage[] = "Usage: syswiz [-f *] [-h] [-p] [-v]";
int parse_options(struct options *opt, int argc, char **argv)
{
// Initialize options
memset(opt, 0, sizeof(struct options));
if (argc == 1) {
printf("%s\n", usage);
return 1;
}
char c;
while ((c = getopt (argc, argv, "f:hp:v")) != -1) {
switch (c) {
case 'f':
opt->file_set = 1;
strncpy(opt->file, optarg, strlen(optarg));
break;
case 'h':
printf("%s\n", usage);
break;
case 'p':
opt->path_set = 1;
strncpy(opt->path, optarg, strlen(optarg));
break;
case 'v':
opt->verbose = 1;
break;
default:
fprintf(stderr, "Unrecognized arg: %c\n", c);
return 1;
}
}
if (opt->file_set) {
if (!verify_path(opt, opt->file)) {
printf("Invalid file: %s\n", opt->file);
return 1;
}
}
if (opt->path_set) {
if (!verify_path(opt, opt->path)) {
printf("Invalid path: %s\n", opt->path);
return 1;
}
}
return 0;
}
void handle_output(struct options *opt, struct output *out)
{
if (out == NULL)
return;
printf("%s", out->buffer);
free(out);
}
int verify_path(struct options * opt, char *path)
{
// Check if path is accessible
if (access(path, F_OK) != -1)
return 1;
// Try path based on current working directory
char *cwd = getcwd(NULL, 0);
char pathbuf[MAX_PATH_LEN];
snprintf(pathbuf, MAX_PATH_LEN, "%s/%s", cwd, path);
if (opt->verbose)
printf("Verifying %s ...\n", pathbuf);
// Check if cwd/path is accessible
if (access(pathbuf, F_OK) != -1 ) {
return 1;
}
// Could not access path
return 0;
}
/*
* Prepares stat struct for output
*/
struct output *format_stat(struct options *opt, struct stat *st)
{
struct output *out;
// Allocate and initialize output structure
out = malloc(sizeof(struct output));
memset(out, 0, sizeof(struct output));
int bytes = snprintf(out->buffer,
MAX_BUF_LEN,
" on device: %i\n"
" inode: %i\n"
" protection: %i\n"
" hard links: %i\n"
" user: %i\n"
" group: %i\n"
" is device: %i\n"
" total size: %i\n"
" block size: %i\n"
" blocks: %i\n"
"last access: %i\n"
"last modify: %i\n"
"last change: %i\n",
(int)st->st_dev,
(int)st->st_ino,
(int)st->st_mode,
(int)st->st_nlink,
(int)st->st_uid,
(int)st->st_gid,
(int)st->st_rdev,
(int)st->st_size,
(int)st->st_blksize,
(int)st->st_blocks,
(int)st->st_atime,
(int)st->st_mtime,
(int)st->st_ctime);
out->buf_len += bytes;
return out;
}
/*
* Recurses to search path for all files matching opt->inode
*/
struct output *find_files(struct options *opt, char *path)
{
DIR *dp;
struct dirent *entry;
struct stat st;
struct output *out;
if ((dp = opendir(path)) == NULL) {
fprintf(stderr, "cannot open directory: %s\n", path);
return NULL;
}
// Fill in inode if needed
if (!opt->inode) {
if (opt->verbose)
printf("Doing stat(%s) ...\n", opt->file);
if (stat(opt->file, &st) == -1) {
fprintf(stderr, "could not stat file: %s\n", opt->file);
return NULL;
}
opt->inode = st.st_ino;
}
chdir(path);
// Allocate and initialize output structure
out = malloc(sizeof(struct output));
memset(out, 0, sizeof(struct output));
while ((entry = readdir(dp)) != NULL) {
lstat(entry->d_name, &st);
if (S_ISDIR(st.st_mode)) {
// Ignore . and ..
if (strcmp(".", entry->d_name) == 0 ||
strcmp("..", entry->d_name) == 0)
continue;
// check for inode match
if (st.st_ino == opt->inode) {
int bytes = snprintf(out->buffer + out->buf_len,
MAX_BUF_LEN - out->buf_len,
"%s\n",
entry->d_name);
out->buf_len += bytes;
}
// Recurse
find_files(opt, entry->d_name);
} else {
// check for inode match
if (st.st_ino == opt->inode) {
int bytes = snprintf(out->buffer + out->buf_len,
MAX_BUF_LEN - out->buf_len,
"%s\n",
entry->d_name);
out->buf_len += bytes;
if (opt->verbose)
printf("Found inode match: %s\n", entry->d_name);
}
}
}
chdir("..");
closedir(dp);
return out;
}
// TODO: add to interface, so command can be approached from a file name
// rather than inode.
// syswiz -f file
// information about the file
// if hardlinked, auto-searching for other fs locations
// ...
// eventually some kind of top-like dynamic view following file changes?
// tail -f + stat + top ?
// TODO: invoking syswiz with no arguments should start an interactive system.
// So syswiz can be used either as a single-execution command fitting with
// the UNIX pipeline model or it can be used as an interactive system
// diagnosic.
// Slightly ambitious, but if the basic double-structure is put in place, then
// this could serve as a good place to put utility routines as they're
// acquired.
// I suppose the tricky part here is to handle updating changes in displayed
// information during interactive mode. So each command has to work with a
// similar output structure so it can be appropriately formated.
// TODO: from a design standpoint, what about basing the interface around the
// concept of identifying a single interesting starting structure?
// A file, an inode, a process, a network port, a network connection, and so
// on ...
// Perhaps the interactive mode involves combining streams of these target-
// oriented commands.
// Need to start putting in some handling for special files.
// Or how about something like a tail -f for metadata?