int main(int argc, \ char** argv) { int c; int option_index = 0; MPI_Init(&argc, &argv); /* Initialize our processing library and related callbacks. */ /* This is a bit of chicken-and-egg problem, because we'd like * to have our rank to filter output messages below but we might * also want to set different libcircle flags based on command line * options -- for now just pass in the default flags */ CIRCLE_global_rank = CIRCLE_init(argc, argv, CIRCLE_DEFAULT_FLAGS); CIRCLE_cb_create(&DCOPY_add_objects); CIRCLE_cb_process(&DCOPY_process_objects); DCOPY_debug_stream = stdout; /* By default, don't perform a conditional copy. */ DCOPY_user_opts.conditional = false; /* By default, don't skip the compare option. */ DCOPY_user_opts.skip_compare = false; /* By default, show info log messages. */ CIRCLE_loglevel CIRCLE_debug = CIRCLE_LOG_INFO; DCOPY_debug_level = DCOPY_LOG_DBG; /* By default, don't unlink destination files if an open() fails. */ DCOPY_user_opts.force = false; /* By default, don't bother to preserve all attributes. */ DCOPY_user_opts.preserve = false; /* By default, don't attempt any type of recursion. */ DCOPY_user_opts.recursive = false; DCOPY_user_opts.recursive_unspecified = false; /* By default, assume the filesystem is reliable (exit on errors). */ DCOPY_user_opts.reliable_filesystem = true; static struct option long_options[] = { {"conditional" , no_argument , 0, 'c'}, {"skip-compare" , no_argument , 0, 'C'}, {"debug" , required_argument, 0, 'd'}, {"force" , no_argument , 0, 'f'}, {"help" , no_argument , 0, 'h'}, {"preserve" , no_argument , 0, 'p'}, {"recursive" , no_argument , 0, 'R'}, {"recursive-unspecified", no_argument , 0, 'r'}, {"unreliable-filesystem", no_argument , 0, 'U'}, {"version" , no_argument , 0, 'v'}, {0 , 0 , 0, 0 } }; /* Parse options */ while((c = getopt_long(argc, argv, "cCd:fhpRrUv", \ long_options, &option_index)) != -1) { switch(c) { case 'c': DCOPY_user_opts.conditional = true; if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Performing a conditional copy."); } break; case 'C': DCOPY_user_opts.skip_compare = true; if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Skipping the comparison stage " \ "(may result in corruption)."); } break; case 'd': if(strncmp(optarg, "fatal", 5)) { CIRCLE_debug = CIRCLE_LOG_FATAL; DCOPY_debug_level = DCOPY_LOG_FATAL; if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Debug level set to: fatal"); } } else if(strncmp(optarg, "err", 3)) { CIRCLE_debug = CIRCLE_LOG_ERR; DCOPY_debug_level = DCOPY_LOG_ERR; if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Debug level set to: errors"); } } else if(strncmp(optarg, "warn", 4)) { CIRCLE_debug = CIRCLE_LOG_WARN; DCOPY_debug_level = DCOPY_LOG_WARN; if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Debug level set to: warnings"); } } else if(strncmp(optarg, "info", 4)) { CIRCLE_debug = CIRCLE_LOG_INFO; DCOPY_debug_level = DCOPY_LOG_INFO; if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Debug level set to: info"); } } else if(strncmp(optarg, "dbg", 4)) { CIRCLE_debug = CIRCLE_LOG_DBG; DCOPY_debug_level = DCOPY_LOG_DBG; if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Debug level set to: debug"); } } else { if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Debug level `%s' not recognized. " \ "Defaulting to `info'.", optarg); } } break; case 'f': DCOPY_user_opts.force = true; if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Deleting destination on errors."); } break; case 'h': if(CIRCLE_global_rank == 0) { DCOPY_print_usage(argv); } DCOPY_exit(EXIT_SUCCESS); break; case 'p': DCOPY_user_opts.preserve = true; if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Preserving file attributes."); } break; case 'R': DCOPY_user_opts.recursive = true; if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Performing correct recursion."); LOG(DCOPY_LOG_WARN, "Warning, only files and directories are implemented."); } break; case 'r': DCOPY_user_opts.recursive_unspecified = true; if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Performing recursion. " \ "Ignoring special files."); } break; case 'U': DCOPY_user_opts.reliable_filesystem = false; if(CIRCLE_global_rank == 0) { LOG(DCOPY_LOG_INFO, "Unreliable filesystem specified. " \ "Retry mode enabled."); } break; case 'v': if(CIRCLE_global_rank == 0) { DCOPY_print_version(); } DCOPY_exit(EXIT_SUCCESS); break; case '?': default: if(CIRCLE_global_rank == 0) { if(optopt == 'd') { DCOPY_print_usage(argv); fprintf(stderr, "Option -%c requires an argument.\n", \ optopt); } else if(isprint(optopt)) { DCOPY_print_usage(argv); fprintf(stderr, "Unknown option `-%c'.\n", optopt); } else { DCOPY_print_usage(argv); fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt); } } DCOPY_exit(EXIT_FAILURE); break; } } /** Parse the source and destination paths. */ DCOPY_parse_path_args(argv, optind, argc); /* initialize linked list of stat objects */ DCOPY_list_head = NULL; DCOPY_list_tail = NULL; /* Initialize our jump table for core operations. */ DCOPY_jump_table[TREEWALK] = DCOPY_do_treewalk; DCOPY_jump_table[COPY] = DCOPY_do_copy; DCOPY_jump_table[CLEANUP] = DCOPY_do_cleanup; DCOPY_jump_table[COMPARE] = DCOPY_do_compare; /* Set the log level for the processing library. */ CIRCLE_enable_logging(CIRCLE_debug); /* Grab a relative and actual start time for the epilogue. */ time(&(DCOPY_statistics.time_started)); DCOPY_statistics.wtime_started = CIRCLE_wtime(); /* Perform the actual file copy. */ CIRCLE_begin(); /* Determine the actual and relative end time for the epilogue. */ DCOPY_statistics.wtime_ended = CIRCLE_wtime(); time(&(DCOPY_statistics.time_ended)); /* Let the processing library cleanup. */ CIRCLE_finalize(); /* set timestamps if needed */ if (DCOPY_user_opts.preserve) { DCOPY_set_timestamps(); } /* free list of stat objects */ DCOPY_stat_elem_t* current = DCOPY_list_head; while (current != NULL) { DCOPY_stat_elem_t* next = current->next; free(current->file); free(current->sb); free(current); current = next; } /* Print the results to the user. */ DCOPY_epilogue(); DCOPY_exit(EXIT_SUCCESS); }
/* check that source and destination paths are valid */ static void DCOPY_check_paths(void) { /* assume path parameters are valid */ int valid = 1; /* just have rank 0 check */ if(DCOPY_global_rank == 0) { /* count number of readable source paths */ int i; int num_readable = 0; for(i = 0; i < num_src_params; i++) { char* path = src_params[i].path; if(mfu_access(path, R_OK) == 0) { num_readable++; } else { /* found a source path that we can't read, not fatal, * but print an error to notify user */ char* orig = src_params[i].orig; MFU_LOG(MFU_LOG_ERR, "Could not read `%s' errno=%d %s", orig, errno, strerror(errno)); } } /* verify that we have at least one source path */ if(num_readable < 1) { MFU_LOG(MFU_LOG_ERR, "At least one valid source must be specified"); valid = 0; goto bcast; } /* * First we need to determine if the last argument is a file or a directory. * We first attempt to see if the last argument already exists on disk. If it * doesn't, we then look at the sources to see if we can determine what the * last argument should be. */ bool dest_exists = false; bool dest_is_dir = false; bool dest_is_file = false; bool dest_is_link_to_dir = false; bool dest_is_link_to_file = false; bool dest_required_to_be_dir = false; /* check whether dest exists, its type, and whether it's writable */ if(dest_param.path_stat_valid) { /* we could stat dest path, so something is there */ dest_exists = true; /* now determine its type */ if(S_ISDIR(dest_param.path_stat.st_mode)) { /* dest is a directory */ dest_is_dir = true; } else if(S_ISREG(dest_param.path_stat.st_mode)) { /* dest is a file */ dest_is_file = true; } else if(S_ISLNK(dest_param.path_stat.st_mode)) { /* dest is a symlink, but to what? */ if (dest_param.target_stat_valid) { /* target of the symlink exists, determine what it is */ if(S_ISDIR(dest_param.target_stat.st_mode)) { /* dest is link to a directory */ dest_is_link_to_dir = true; } else if(S_ISREG(dest_param.target_stat.st_mode)) { /* dest is link to a file */ dest_is_link_to_file = true; } else { /* unsupported type */ MFU_LOG(MFU_LOG_ERR, "Unsupported filetype `%s' --> `%s'", dest_param.orig, dest_param.target); valid = 0; goto bcast; } } else { /* dest is a link, but its target does not exist, * consider this an error */ MFU_LOG(MFU_LOG_ERR, "Destination is broken symlink `%s'", dest_param.orig); valid = 0; goto bcast; } } else { /* unsupported type */ MFU_LOG(MFU_LOG_ERR, "Unsupported filetype `%s'", dest_param.orig); valid = 0; goto bcast; } /* check that dest is writable */ if(mfu_access(dest_param.path, W_OK) < 0) { MFU_LOG(MFU_LOG_ERR, "Destination is not writable `%s'", dest_param.path); valid = 0; goto bcast; } } else { /* destination does not exist, so we'll be creating it, * check that its parent is writable */ /* compute parent path */ mfu_path* parent = mfu_path_from_str(dest_param.path); mfu_path_dirname(parent); char* parent_str = mfu_path_strdup(parent); mfu_path_delete(&parent); /* check that parent is writable */ if(mfu_access(parent_str, W_OK) < 0) { MFU_LOG(MFU_LOG_ERR, "Destination parent directory is not writable `%s'", parent_str); valid = 0; mfu_free(&parent_str); goto bcast; } mfu_free(&parent_str); } /* determine whether caller *requires* copy into dir */ /* TODO: if caller specifies dest/ or dest/. */ /* if caller specifies more than one source, * then dest has to be a directory */ if(num_src_params > 1) { dest_required_to_be_dir = true; } /* if caller requires dest to be a directory, and if dest does not * exist or it does it exist but it's not a directory, then abort */ if(dest_required_to_be_dir && (!dest_exists || (!dest_is_dir && !dest_is_link_to_dir))) { MFU_LOG(MFU_LOG_ERR, "Destination is not a directory `%s'", dest_param.orig); valid = 0; goto bcast; } /* we copy into a directory if any of the following: * 1) user specified more than one source * 2) destination already exists and is a directory * 3) destination already exists and is a link to a directory */ bool copy_into_dir = (dest_required_to_be_dir || dest_is_dir || dest_is_link_to_dir); DCOPY_user_opts.copy_into_dir = copy_into_dir ? 1 : 0; } /* get status from rank 0 */ bcast: MPI_Bcast(&valid, 1, MPI_INT, 0, MPI_COMM_WORLD); /* exit job if we found a problem */ if(! valid) { if(DCOPY_global_rank == 0) { MFU_LOG(MFU_LOG_ERR, "Exiting run"); } MPI_Barrier(MPI_COMM_WORLD); DCOPY_exit(EXIT_FAILURE); } /* rank 0 broadcasts whether we're copying into a directory */ MPI_Bcast(&DCOPY_user_opts.copy_into_dir, 1, MPI_INT, 0, MPI_COMM_WORLD); }