pfuse 0.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/fusefs_lib.c ADDED
@@ -0,0 +1,560 @@
1
+ // ruby-fuse
2
+ // A Ruby module to interact with the FUSE userland filesystem in
3
+ // a clear way.
4
+
5
+ // TODO
6
+ // * return values are inconsistent
7
+ // * fill in the rest of the fuse api
8
+ // * turn half of these into macros
9
+ // * sample FS for testing
10
+
11
+ #include "fusefs_lib.h"
12
+
13
+ // rf_getattr - FuseRoot.getattr should return a FuseFS::Stat object
14
+ static int
15
+ rf_getattr(const char *path, struct stat *stbuf)
16
+ {
17
+ VALUE cur_entry;
18
+
19
+ debug("rf_getattr(%s)\n", path);
20
+
21
+ memset(stbuf, 0, sizeof(struct stat));
22
+ cur_entry = rf_call("getattr", path, Qnil);
23
+
24
+ if (RTEST(cur_entry)) {
25
+ VSTAT2STAT(cur_entry, stbuf);
26
+ return 0;
27
+ } else {
28
+ return -ENOENT;
29
+ }
30
+ }
31
+
32
+ // rf_readlink - resolve symlink then read bufsize into buf
33
+
34
+ // rf_mknod - make a special file node
35
+ static int
36
+ rf_mknod(const char *path, mode_t umode, dev_t rdev)
37
+ {
38
+ debug("rf_mknod(%s, 0%o)\n", path, umode);
39
+
40
+ // We ONLY permit regular files. No blocks, characters, fifos, etc.
41
+ if (!S_ISREG(umode)) {
42
+ return -EACCES;
43
+ }
44
+
45
+ // FuseFS.mknod only takes a path, since we don't handle non-files as above
46
+ if (!RTEST(rf_call("mknod", path, Qnil)))
47
+ return -EACCES;
48
+
49
+ return 0;
50
+ }
51
+
52
+ // rf_mkdir - make a directory file
53
+ static int
54
+ rf_mkdir(const char *path, mode_t mode)
55
+ {
56
+ VALUE ret;
57
+
58
+ debug("rf_mkdir(%s, 0%o)\n", path, mode);
59
+
60
+ ret = rf_call("mkdir", path, UINT2NUM(mode));
61
+
62
+ if (RTEST(ret))
63
+ return NUM2INT(ret);
64
+
65
+ return -ENOENT;
66
+ }
67
+
68
+ // rf_unlink - remove directory entry
69
+ static int
70
+ rf_unlink(const char *path)
71
+ {
72
+ debug("rf_unlink(%s)\n", path);
73
+
74
+ if (!RTEST(rf_call("unlink", path, Qnil)))
75
+ return -ENOENT;
76
+
77
+ return 0;
78
+ }
79
+
80
+ // rf_rmdir - remove a directory file
81
+ static int
82
+ rf_rmdir(const char *path)
83
+ {
84
+ VALUE ret;
85
+
86
+ debug("rf_mkdir(%s)\n", path);
87
+
88
+ ret = rf_call("rmdir", path, Qnil);
89
+
90
+ if (RTEST(ret))
91
+ return NUM2INT(ret);
92
+
93
+ return -ENOENT;
94
+ }
95
+
96
+ // rf_symlink - make a symbolic link to a file
97
+ static int
98
+ rf_symlink(const char *path, const char *dest)
99
+ {
100
+ VALUE args;
101
+ VALUE ret;
102
+
103
+ debug("rf_symlink(%s, %s)\n", path, dest);
104
+
105
+ args = rb_ary_new();
106
+ rb_ary_push(args, rb_str_new2(dest));
107
+
108
+ ret = rf_call("symlink", path, args);
109
+
110
+ if (RTEST(ret))
111
+ return NUM2INT(ret);
112
+
113
+ return -ENOENT;
114
+ }
115
+
116
+ // rf_rename - change the name of a file
117
+ static int
118
+ rf_rename(const char *path, const char *dest)
119
+ {
120
+ VALUE ret;
121
+ VALUE args;
122
+
123
+ debug("rf_rename(%s, %s)\n", path, dest);
124
+
125
+ args = rb_ary_new();
126
+ rb_ary_push(args, rb_str_new2(dest));
127
+
128
+ ret = rf_call("rename", path, args);
129
+
130
+ if (RTEST(ret))
131
+ return NUM2INT(ret);
132
+
133
+ return 0;
134
+ }
135
+
136
+ // rf_link - make a hard file link
137
+ static int
138
+ rf_link(const char *path, const char *dest)
139
+ {
140
+ VALUE args;
141
+ VALUE ret;
142
+
143
+ debug("rf_link(%s, %s)\n", path, dest);
144
+
145
+ args = rb_ary_new();
146
+ rb_ary_push(args, rb_str_new2(dest));
147
+
148
+ ret = rf_call("link", path, args);
149
+ if (RTEST(ret))
150
+ return NUM2INT(ret);
151
+
152
+ return 0;
153
+ }
154
+
155
+ // rf_chmod - change mode of file
156
+ static int
157
+ rf_chmod(const char *path, mode_t mode)
158
+ {
159
+ VALUE ret;
160
+
161
+ debug("rf_chmod(%s, %o)\n", path, mode);
162
+
163
+ ret = rf_call("chmod", path, INT2NUM(mode));
164
+
165
+ if (RTEST(ret))
166
+ return NUM2INT(ret);
167
+
168
+ return -ENOENT;
169
+ }
170
+
171
+ // rf_chown - change owner and group of a file
172
+
173
+ // rf_truncate - truncate or extend a file to a specified length
174
+ static int
175
+ rf_truncate(const char *path, off_t offset)
176
+ {
177
+ VALUE ret;
178
+
179
+ debug("rf_truncate(%s, %d)\n", path, offset);
180
+
181
+ ret = rf_call("truncate", path, ULONG2NUM(offset));
182
+
183
+ if (RTEST(ret))
184
+ return NUM2INT(ret);
185
+
186
+ return -ENOENT;
187
+ }
188
+
189
+ // rf_utime - set file times
190
+ static int
191
+ rf_utime(const char *path, struct utimbuf *ubuf)
192
+ {
193
+ VALUE ret;
194
+ VALUE args;
195
+
196
+ args = rb_ary_new();
197
+ rb_ary_push(args, LONG2NUM(ubuf->actime));
198
+ rb_ary_push(args, LONG2NUM(ubuf->modtime));
199
+
200
+ ret = rf_call("utime", path, args);
201
+
202
+ if (RTEST(ret))
203
+ return ret;
204
+
205
+ // default to non-error because I don't see myself using this much
206
+ return 0;
207
+ }
208
+
209
+ // rf_open - open or create a file for reading or writing
210
+ // opening the file will probably be a noop for most backends
211
+ static int
212
+ rf_open(const char *path, struct fuse_file_info *fi)
213
+ {
214
+ char open_opts[4], *optr;
215
+
216
+ debug("rf_open(%s)\n", path);
217
+
218
+ optr = open_opts;
219
+ switch (fi->flags & 3) {
220
+ case 0:
221
+ *(optr++) = 'r';
222
+ break;
223
+ case 1:
224
+ *(optr++) = 'w';
225
+ break;
226
+ case 2:
227
+ *(optr++) = 'w';
228
+ *(optr++) = 'r';
229
+ break;
230
+ default:
231
+ debug("Opening a file with something other than rd, wr, or rdwr?");
232
+ }
233
+
234
+ if (fi->flags & O_APPEND)
235
+ *(optr++) = 'a';
236
+ *(optr) = '\0';
237
+
238
+ if (!RTEST(rf_call("open", path, rb_str_new2(open_opts)))) {
239
+ return -ENOENT;
240
+ }
241
+
242
+ return 0;
243
+ }
244
+
245
+ // rf_read - read input
246
+ static int
247
+ rf_read(
248
+ const char *path,
249
+ char *buf,
250
+ size_t size,
251
+ off_t offset,
252
+ struct fuse_file_info *fi)
253
+ {
254
+ VALUE ret;
255
+ VALUE args;
256
+
257
+ debug("rf_read(%s)\n", path);
258
+
259
+ args = rb_ary_new();
260
+ rb_ary_push(args, LONG2NUM(offset));
261
+ rb_ary_push(args, LONG2NUM(size));
262
+
263
+ ret = rf_call("read", path, args);
264
+
265
+ if (!RTEST(ret))
266
+ return 0;
267
+
268
+ if (TYPE(ret) != T_STRING)
269
+ return 0;
270
+
271
+ memcpy(buf, RSTRING(ret)->ptr, RSTRING(ret)->len);
272
+ return RSTRING(ret)->len;
273
+ }
274
+
275
+ // rf_write - write output
276
+ static int
277
+ rf_write(
278
+ const char *path,
279
+ const char *buf,
280
+ size_t size,
281
+ off_t offset,
282
+ struct fuse_file_info *fi)
283
+ {
284
+ VALUE ret;
285
+ VALUE args;
286
+
287
+ debug("rf_write(%s)\n", path);
288
+
289
+ args = rb_ary_new();
290
+ rb_ary_push(args, ULONG2NUM(offset));
291
+ rb_ary_push(args, ULONG2NUM(size));
292
+ rb_ary_push(args, rb_str_new(buf, size));
293
+
294
+ ret = rf_call("write", path, args);
295
+
296
+ if (RTEST(ret))
297
+ return NUM2INT(ret);
298
+
299
+ return 0UL;
300
+ }
301
+
302
+ // rf_statfs - get filesystem statistics
303
+ static int
304
+ rf_statfs(const char *path, struct statvfs *stbuf)
305
+ {
306
+ return 0;
307
+ // VALUE cur_entry;
308
+
309
+ // debug("rf_statfs(%s)\n", path);
310
+
311
+ // cur_entry = rf_call("statfs", path, Qnil);
312
+
313
+ // if (RTEST(cur_entry)) {
314
+ // VSTATVFS2STATVFS(cur_entry, stbuf);
315
+ // return 0;
316
+ // } else {
317
+ // return -ENOENT;
318
+ // }
319
+ }
320
+
321
+ // rf_flush - close file (after system calls close())
322
+
323
+ // rf_release - close file (the final flush)
324
+ static int
325
+ rf_release(const char *path, struct fuse_file_info *fi)
326
+ {
327
+ debug("rf_release(%s)\n", path);
328
+
329
+ if (!RTEST(rf_call("release", path, Qnil)))
330
+ return -ENOENT;
331
+
332
+ return 0;
333
+ }
334
+
335
+ // rf_fsync - synchronise a file's in-core state with that on disk
336
+
337
+ // rf_setxattr - set an extended attribute value
338
+
339
+ // rf_getxattr - get an extended attribute value
340
+
341
+ // rf_listxattr - list extended attribute names
342
+
343
+ // rf_removexattr - remove an extended attribute value
344
+
345
+ // rf_opendir - check if opendir is permitted for this directory
346
+
347
+ // rf_readdir - an array of FuseFS::Stat objects sent to fill_dir
348
+ static int
349
+ rf_readdir(
350
+ const char *path,
351
+ void *buf,
352
+ fuse_fill_dir_t filler,
353
+ off_t offset,
354
+ struct fuse_file_info *fi)
355
+ {
356
+ VALUE cur_entry;
357
+ VALUE retval;
358
+ struct stat *stbuf;
359
+
360
+ debug("rf_readdir(%s)\n", path);
361
+
362
+ stbuf = malloc(sizeof(struct stat));
363
+
364
+ // TODO maybe fi is interesting
365
+ // This is what fuse does to turn off 'unused' warnings.
366
+ (void) offset;
367
+ (void) fi;
368
+
369
+ // filler takes (void *dh_, const char *name, const struct stat *statp, off_t off)
370
+ // you can pass in NULL statp for defaults
371
+ // you can pass in 0 off for magic
372
+
373
+ // These two are always in a directory
374
+ filler(buf, ".", NULL, 0);
375
+ filler(buf, "..", NULL, 0);
376
+
377
+ retval = rf_call("readdir", path, Qnil);
378
+ if (!RTEST(retval) || (TYPE(retval) != T_ARRAY)) {
379
+ return 0;
380
+ }
381
+
382
+ while ((cur_entry = rb_ary_shift(retval)) != Qnil) {
383
+ VSTAT2STAT(cur_entry, stbuf);
384
+ filler(buf, STR2CSTR(rb_funcall(cur_entry, rb_intern("name"), 0)), stbuf, 0);
385
+ }
386
+
387
+ return 0;
388
+ }
389
+
390
+ // rf_releasedir - release dir (file returned from opendir is passed in)
391
+
392
+ // rf_fsyncdir - sync everything in the dir
393
+ static int
394
+ rf_fsyncdir(const char * path, int p, struct fuse_file_info *fi)
395
+ {
396
+ VALUE ret;
397
+ VALUE args;
398
+
399
+ // non-0 p means user data only, not metadata
400
+ args = rb_ary_new();
401
+ rb_ary_push(args, INT2NUM(p));
402
+
403
+ ret = rf_call("fsyncdir", path, args);
404
+
405
+ if (RTEST(ret))
406
+ return NUM2INT(ret);
407
+
408
+ // default to non-error because I don't see myself using this much
409
+ return 0;
410
+ }
411
+
412
+ // rf_access - check access permissions of a file or pathname
413
+ static int
414
+ rf_access(const char *path, int mask)
415
+ {
416
+ debug("rf_access(%s, %d)\n", path, mask);
417
+ return 0;
418
+ }
419
+
420
+ // rf_create - create and open a file
421
+
422
+ // rf_ftruncate - truncate() but called from system ftruncate()
423
+
424
+ // rf_fgetattr - getattr() but called if rf_create() is implemented
425
+
426
+ // rf_lock - perform POSIX file locking operation
427
+
428
+ // rf_utimens - utime with nanosecond resolution
429
+
430
+ // rf_bmap - map block index within file to block index within device
431
+
432
+ // rf_flag_nullpath_ok - accept NULL path
433
+
434
+
435
+ // rf_oper
436
+ static struct fuse_operations rf_oper = {
437
+ .getattr = rf_getattr,
438
+ .readdir = rf_readdir,
439
+ .mknod = rf_mknod,
440
+ .mkdir = rf_mkdir,
441
+ .unlink = rf_unlink,
442
+ .rmdir = rf_rmdir,
443
+ .rename = rf_rename,
444
+ .truncate = rf_truncate,
445
+ .utime = rf_utime,
446
+ .open = rf_open,
447
+ .read = rf_read,
448
+ .write = rf_write,
449
+ .statfs = rf_statfs,
450
+ .release = rf_release,
451
+ .fsyncdir = rf_fsyncdir,
452
+ };
453
+
454
+ static struct fuse_operations rf_unused_oper = {
455
+ .symlink = rf_symlink,
456
+ .access = rf_access,
457
+ .link = rf_link,
458
+ .chmod = rf_chmod,
459
+ };
460
+
461
+ // rf_set_root
462
+ VALUE
463
+ rf_set_root(VALUE self, VALUE rootval)
464
+ {
465
+ if (self != cFuseFS) {
466
+ rb_raise(cFSException, "Error: 'set_root' called outside of FuseFS?!");
467
+ return Qnil;
468
+ }
469
+
470
+ rb_iv_set(cFuseFS, "@root", rootval);
471
+ FuseRoot = rootval;
472
+
473
+ return Qtrue;
474
+ }
475
+
476
+ // rf_mount_to
477
+ VALUE
478
+ rf_mount_to(int argc, VALUE *argv, VALUE self)
479
+ {
480
+ struct fuse_args *opts;
481
+ VALUE mountpoint;
482
+ int i;
483
+ char *cur;
484
+
485
+ if (self != cFuseFS) {
486
+ rb_raise(cFSException, "Error: 'mount_to' called outside of FuseFS?!");
487
+ return Qnil;
488
+ }
489
+
490
+ if (argc == 0) {
491
+ rb_raise(rb_eArgError, "mount_to requires at least 1 argument!");
492
+ return Qnil;
493
+ }
494
+
495
+ mountpoint = argv[0];
496
+
497
+ Check_Type(mountpoint, T_STRING);
498
+ opts = ALLOC(struct fuse_args);
499
+ opts->argc = argc;
500
+ opts->argv = ALLOC_N(char *, opts->argc);
501
+ opts->allocated = 1;
502
+
503
+ // TODO why
504
+ opts->argv[0] = strdup("-odirect_io");
505
+
506
+ for (i = 1; i < argc; i++) {
507
+ cur = StringValuePtr(argv[i]);
508
+ opts->argv[i] = ALLOC_N(char, RSTRING(argv[i])->len + 2);
509
+ sprintf(opts->argv[i], "-o%s", cur);
510
+ }
511
+
512
+ // TODO debug(rf_unused_oper);
513
+ (void) rf_unused_oper;
514
+
515
+ rb_iv_set(cFuseFS, "@mountpoint", mountpoint);
516
+ fusefs_setup(STR2CSTR(mountpoint), &rf_oper, opts);
517
+
518
+ return Qtrue;
519
+ }
520
+
521
+ // rf_fd
522
+ VALUE
523
+ rf_fd(VALUE self)
524
+ {
525
+ int fd = fusefs_fd();
526
+ if (fd < 0)
527
+ return Qnil;
528
+ return INT2NUM(fd);
529
+ }
530
+
531
+ // rf_process
532
+ VALUE
533
+ rf_process(VALUE self)
534
+ {
535
+ fusefs_process();
536
+ return Qnil;
537
+ }
538
+
539
+ // Init_fusefs_lib()
540
+ void
541
+ Init_fusefs_lib() {
542
+ init_time = time(NULL);
543
+
544
+ /* module FuseFS */
545
+ cFuseFS = rb_define_module("FuseFS");
546
+
547
+ /* Our exception */
548
+ cFSException = rb_define_class_under(cFuseFS, "FuseFSException",
549
+ rb_eStandardError);
550
+
551
+ /* def Fuse.run */
552
+ rb_define_singleton_method(cFuseFS, "fuse_fd", rf_fd, 0);
553
+ rb_define_singleton_method(cFuseFS, "process", rf_process, 0);
554
+ rb_define_singleton_method(cFuseFS, "mount_to", rf_mount_to, -1);
555
+ rb_define_singleton_method(cFuseFS, "mount_under", rf_mount_to, -1);
556
+ rb_define_singleton_method(cFuseFS, "mountpoint", rf_mount_to, -1);
557
+ rb_define_singleton_method(cFuseFS, "set_root", rf_set_root, 1);
558
+ rb_define_singleton_method(cFuseFS, "root=", rf_set_root, 1);
559
+ }
560
+
data/ext/fusefs_lib.h ADDED
@@ -0,0 +1,119 @@
1
+ #define FUSE_USE_VERSION 26
2
+ #define _FILE_OFFSET_BITS 64
3
+
4
+ #include <fuse.h>
5
+ #include <stdio.h>
6
+ #include <string.h>
7
+ #include <errno.h>
8
+ #include <sys/types.h>
9
+ #include <sys/stat.h>
10
+ #include <fcntl.h>
11
+ #include <unistd.h>
12
+ #include <ruby.h>
13
+ #include <stdarg.h>
14
+
15
+ #include "fusefs_fuse.h"
16
+
17
+ #define VSTAT2STAT(cur_entry, stbuf) do {\
18
+ stbuf->st_mode = NUM2INT (rb_funcall(cur_entry, rb_intern("st_mode"), 0)); \
19
+ stbuf->st_nlink = NUM2INT (rb_funcall(cur_entry, rb_intern("st_nlink"), 0)); \
20
+ stbuf->st_size = NUM2LONG (rb_funcall(cur_entry, rb_intern("st_size"), 0)); \
21
+ stbuf->st_uid = NUM2INT (rb_funcall(cur_entry, rb_intern("st_uid"), 0)); \
22
+ stbuf->st_gid = NUM2INT (rb_funcall(cur_entry, rb_intern("st_gid"), 0)); \
23
+ stbuf->st_mtime = NUM2ULONG(rb_funcall(cur_entry, rb_intern("st_mtime"), 0)); \
24
+ stbuf->st_atime = NUM2ULONG(rb_funcall(cur_entry, rb_intern("st_atime"), 0)); \
25
+ stbuf->st_ctime = NUM2ULONG(rb_funcall(cur_entry, rb_intern("st_ctime"), 0)); \
26
+ } while (0)
27
+
28
+ #define VSTATVFS2STATVFS(cur_entry, stbuf) do {\
29
+ memset(stbuf, 0, sizeof(struct statvfs)); \
30
+ stbuf->f_bsize = NUM2INT(rb_funcall(cur_entry, rb_intern("f_bsize"), 0)); \
31
+ stbuf->f_frsize = NUM2INT(rb_funcall(cur_entry, rb_intern("f_frsize"), 0)); \
32
+ stbuf->f_blocks = NUM2INT(rb_funcall(cur_entry, rb_intern("f_blocks"), 0)); \
33
+ stbuf->f_bfree = NUM2INT(rb_funcall(cur_entry, rb_intern("f_bfree"), 0)); \
34
+ stbuf->f_bavail = NUM2INT(rb_funcall(cur_entry, rb_intern("f_bavail"), 0)); \
35
+ stbuf->f_files = NUM2INT(rb_funcall(cur_entry, rb_intern("f_files"), 0)); \
36
+ stbuf->f_ffree = NUM2INT(rb_funcall(cur_entry, rb_intern("f_ffree"), 0)); \
37
+ stbuf->f_favail = NUM2INT(rb_funcall(cur_entry, rb_intern("f_favail"), 0)); \
38
+ stbuf->f_fsid = NUM2INT(rb_funcall(cur_entry, rb_intern("f_fsid"), 0)); \
39
+ stbuf->f_flag = NUM2INT(rb_funcall(cur_entry, rb_intern("f_flag"), 0)); \
40
+ stbuf->f_namemax = NUM2INT(rb_funcall(cur_entry, rb_intern("f_namemax"), 0)); \
41
+ } while (0)
42
+
43
+ // Ruby Constants constants
44
+ VALUE cFuseFS = Qnil; /* FuseFS class */
45
+ VALUE cFSException = Qnil; /* Our Exception. */
46
+ VALUE FuseRoot = Qnil; /* The root object we call */
47
+
48
+ typedef unsigned long int (*rbfunc)();
49
+
50
+ // file created, modified timestamp
51
+ // TODO no
52
+ time_t init_time;
53
+
54
+ // debug()
55
+ static void
56
+ debug(char *msg,...)
57
+ {
58
+ va_list ap;
59
+ va_start(ap,msg);
60
+ vfprintf(stdout,msg,ap);
61
+ }
62
+
63
+ // rf_protected and rf_call
64
+ //
65
+ // Used for: protection.
66
+ //
67
+ // This is called by rb_protect, and will make a call using
68
+ // the above rb_path and to_call ID to call the method safely
69
+ // on FuseRoot.
70
+ //
71
+ // We call rf_call(path,method_id), and rf_call will use rb_protect
72
+ // to call rf_protected, which makes the call on FuseRoot and returns
73
+ // whatever the call returns.
74
+ static VALUE
75
+ rf_protected(VALUE args)
76
+ {
77
+ ID to_call = SYM2ID(rb_ary_shift(args));
78
+ return rb_apply(FuseRoot,to_call,args);
79
+ }
80
+
81
+ static VALUE
82
+ rf_call(char *methname, const char *path, VALUE arg)
83
+ {
84
+ int error;
85
+ VALUE result;
86
+ VALUE methargs;
87
+ ID method;
88
+
89
+ method = rb_intern(methname);
90
+
91
+ if (arg == Qnil) {
92
+ debug(" root.%s(%s)\n", methname, path);
93
+ } else {
94
+ debug(" root.%s(%s,...)\n", methname, path);
95
+ }
96
+
97
+ if (TYPE(arg) == T_ARRAY) {
98
+ methargs = arg;
99
+ } else if (arg != Qnil) {
100
+ methargs = rb_ary_new();
101
+ rb_ary_push(methargs,arg);
102
+ } else {
103
+ methargs = rb_ary_new();
104
+ }
105
+
106
+ rb_ary_unshift(methargs,rb_str_new2(path));
107
+ rb_ary_unshift(methargs,ID2SYM(method));
108
+
109
+ /* Set up the call and make it. */
110
+ result = rb_protect(rf_protected, methargs, &error);
111
+
112
+ /* Did it error? */
113
+ if (error) {
114
+ return Qnil;
115
+ }
116
+
117
+ return result;
118
+ }
119
+
data/hello.sh ADDED
@@ -0,0 +1,10 @@
1
+ #!/bin/sh
2
+ mkdir -p hello
3
+ ruby -rsample/hello -e "puts 'starting...'" hello &
4
+ echo $? > pid
5
+ sleep 1
6
+ cat hello/hello.txt
7
+ # umount hello/
8
+ sleep 1
9
+ cat pid | xargs kill
10
+ rmdir hello