Project

General

Profile

Download (6.84 KB) Statistics
| Branch: | Tag: | Revision:
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;
}