root/software/fss-parallel-tools/parrm.c @ 6267e2f6
| 7e8045d8 | David Sorber | /**
|
|
* Copyright © 2019, Oracle and/or its affiliates. All rights reserved.
|
|||
*
|
|||
* Licensed under the Universal Permissive License v 1.0 as shown at
|
|||
* http://oss.oracle.com/licenses/upl
|
|||
*
|
|||
*/
|
|||
#include "malloc.h"
|
|||
#include "putils.h"
|
|||
#include "version.h"
|
|||
#include "sched.h"
|
|||
#include "vector-of-strings.h"
|
|||
#include <assert.h>
|
|||
#include <dirent.h>
|
|||
#include <errno.h>
|
|||
#include <pthread.h>
|
|||
#include <string.h>
|
|||
#include <sys/stat.h>
|
|||
#include <unistd.h>
|
|||
static const char *cmd;
|
|||
enum { default_n_threads = 32 };
|
|||
static size_t n_threads = default_n_threads;
|
|||
void help(int exitcode) {
|
|||
FILE *out = exitcode ? stderr : stdout;
|
|||
fprintf(out, "Usage: %s [OPTION]... [DIR]...\n", cmd);
|
|||
fprintf(out, "Recursive remove of a directory hierarchy, done in parallel for performance.\n");
|
|||
fprintf(out, " OPTION can be\n");
|
|||
fprintf(out, " -h, --help print help.\n");
|
|||
fprintf(out, " -P P (capital P) use P-fold parallelism (default %d threads).\n", default_n_threads);
|
|||
fprintf(out, " -- remaining arguments are files even if they start with '-'.\n");
|
|||
fprintf(out, " --version print program version\n");
|
|||
fprintf(out, " --history print a brief history of the versions\n");
|
|||
fprintf(out, "This is version %s gitrev %s compiled %s %s.\n",
|
|||
VERSION, gitrev, __DATE__, __TIME__);
|
|||
fprintf(out, "Report bugs to parallel-tools-support_ww_grp@oracle.com\n");
|
|||
exit(exitcode);
|
|||
}
|
|||
static magic_t rm_magic = "rm magic";
|
|||
struct rmframe {
|
|||
magic_t *magic;
|
|||
char *filename;
|
|||
struct vector_of_strings *files_in_dir;
|
|||
size_t inlet_sum;
|
|||
size_t sum;
|
|||
};
|
|||
static struct rmframe *
|
|||
mk_rmframe(char *filename) {
|
|||
struct rmframe *MALLOC(rf);
|
|||
*rf = (struct rmframe){.magic = &rm_magic,
|
|||
.filename = filename,
|
|||
.files_in_dir = NULL,
|
|||
.inlet_sum = 0,
|
|||
.sum = 0};
|
|||
return rf;
|
|||
}
|
|||
static struct rmframe *rmframe_free(struct rmframe *rf) {
|
|||
FREE(rf->filename);
|
|||
if (rf->files_in_dir) {
|
|||
rf->files_in_dir = vector_of_strings_destroy(rf->files_in_dir);
|
|||
}
|
|||
FREE(rf);
|
|||
return NULL;
|
|||
}
|
|||
static struct rmframe *rmframe_cast(void *app_frame) {
|
|||
struct rmframe *rf = app_frame;
|
|||
assert(rf->magic == &rm_magic);
|
|||
return rf;
|
|||
}
|
|||
static void rmfun_return_inlet(void *parent_app_frame, size_t result) {
|
|||
struct rmframe *rf = rmframe_cast(parent_app_frame);
|
|||
rf->inlet_sum += result;
|
|||
}
|
|||
static struct frame *rmfun_have_dirlist(struct frame *frame, void *app_frame);
|
|||
static struct frame *rmfun_sync(struct frame *frame, void *app_frame);
|
|||
static struct frame *rmfun(struct frame *frame, void *app_frame) {
|
|||
struct rmframe *rf = rmframe_cast(app_frame);
|
|||
struct stat statbuf;
|
|||
{
|
|||
int r = lstat(rf->filename, &statbuf);
|
|||
if (r != 0) {
|
|||
fprintf(stderr, "%s: cannot stat '%s': %s\n", cmd, rf->filename, strerror(errno));
|
|||
rf->sum++;
|
|||
return sched_sync(frame, rmfun_sync);
|
|||
}
|
|||
}
|
|||
if (S_ISDIR(statbuf.st_mode)) {
|
|||
assert(rf->files_in_dir == NULL);
|
|||
rf->files_in_dir = mk_vector_of_strings();
|
|||
DIR *dir = opendir(rf->filename);
|
|||
if (dir == NULL) {
|
|||
fprintf(stderr, "%s: A cannot remove '%s': %s\n", cmd, rf->filename, strerror(errno));
|
|||
rf->sum++;
|
|||
return sched_sync(frame, rmfun_sync);
|
|||
} else {
|
|||
struct dirent *entry;
|
|||
while ((entry = readdir(dir))) {
|
|||
if (strcmp(entry->d_name, ".") == 0) continue;
|
|||
if (strcmp(entry->d_name, "..") == 0) continue;
|
|||
char *path = pathcat(rf->filename, entry->d_name);
|
|||
vector_of_strings_push(rf->files_in_dir, path);
|
|||
FREE(path);
|
|||
}
|
|||
closedir(dir);
|
|||
return rmfun_have_dirlist(frame, app_frame);
|
|||
}
|
|||
} else {
|
|||
if (0) {
|
|||
printf("sleep start\n");
|
|||
sleep(2);
|
|||
printf("sleep end\n");
|
|||
}
|
|||
int r = unlink(rf->filename);
|
|||
if (r != 0) {
|
|||
fprintf(stderr, "%s: B cannot remove '%s': %s\n", cmd, rf->filename, strerror(errno));
|
|||
rf->sum++;
|
|||
}
|
|||
return sched_sync(frame, rmfun_sync);
|
|||
}
|
|||
}
|
|||
static struct frame *rmfun_have_dirlist(struct frame *frame, void *app_frame) {
|
|||
struct rmframe *rf = rmframe_cast(app_frame);
|
|||
assert(rf->files_in_dir != NULL);
|
|||
char *fname;
|
|||
while ((fname = vector_of_strings_pop(rf->files_in_dir))) {
|
|||
struct rmframe *subframe = mk_rmframe(fname);
|
|||
return rmfun(sched_spawn(frame, rmfun_have_dirlist, subframe, rmfun_return_inlet),
|
|||
subframe);
|
|||
}
|
|||
return sched_sync(frame, rmfun_sync);
|
|||
}
|
|||
static struct frame *rmfun_sync(struct frame *frame, void *app_frame) {
|
|||
struct rmframe *rf = rmframe_cast(app_frame);
|
|||
size_t sum = rf->inlet_sum + rf->sum;
|
|||
if (rf->filename && rf->files_in_dir && sum == 0) {
|
|||
// If NULL then there is no dir. We don't delete the parents
|
|||
// of the files mentioned on the command line argument.
|
|||
int r = rmdir(rf->filename);
|
|||
if (r != 0) {
|
|||
fprintf(stderr, "%s: C cannot remove '%s': %s\n", cmd, rf->filename, strerror(errno));
|
|||
sum++;
|
|||
}
|
|||
}
|
|||
rf = rmframe_free(rf);
|
|||
return sched_return(frame, sum);
|
|||
}
|
|||
static int do_rm(struct vector_of_strings *fargs) {
|
|||
struct rmframe *rf = mk_rmframe(NULL);
|
|||
assert(rf->files_in_dir == NULL);
|
|||
rf->files_in_dir = fargs;
|
|||
size_t n = prun(n_threads, rmfun_have_dirlist, rf);
|
|||
if (0) fprintf(stderr, "Couldn't delete %zu objects\n", n);
|
|||
return n==0;
|
|||
}
|
|||
static void
|
|||
parse_args(int argc, const char *argv[], struct vector_of_strings *fargs) {
|
|||
assert(argc > 0);
|
|||
cmd = argv[0];
|
|||
argc--; argv++;
|
|||
while (argc) {
|
|||
if (strcmp(argv[0], "-P") == 0) {
|
|||
argc--; argv++;
|
|||
if (argc == 0) help(1);
|
|||
n_threads = parse_number_or_help(argv[0]);
|
|||
} else if (strcmp(argv[0], "-h") == 0 ||
|
|||
strcmp(argv[0], "--help") == 0) {
|
|||
help(0);
|
|||
} else if (strcmp(argv[0], "--version") == 0) {
|
|||
printf("%s %s\n"
|
|||
"Copyright (C) 2019 Oracle.\n"
|
|||
"Written by Bradley C. Kuszmaul\n",
|
|||
cmd, VERSION);
|
|||
exit(0);
|
|||
} else if (strcmp(argv[0], "--history") == 0) {
|
|||
fprintf(stderr, "%s", VERSION_HISTORY);
|
|||
exit(0);
|
|||
} else if (strcmp(argv[0], "--") == 0) {
|
|||
argc--; argv++;
|
|||
while (argc) {
|
|||
vector_of_strings_push(fargs, argv[0]);
|
|||
argc--; argv++;
|
|||
}
|
|||
} else {
|
|||
vector_of_strings_push(fargs, argv[0]);
|
|||
}
|
|||
argc--; argv++;
|
|||
}
|
|||
}
|
|||
int main (int argc, const char *argv[]) {
|
|||
struct vector_of_strings *fargs = mk_vector_of_strings();
|
|||
parse_args(argc, argv, fargs);
|
|||
if (!do_rm(fargs)) {
|
|||
exit(1);
|
|||
}
|
|||
return 0;
|
|||
}
|