/*
 * cp_nasd.c
 *
 * copies all the files in the specified dir to a NASD drive.  outputs
 * to stdout a mapping between filenames and NASD ids.  runs colocated.
 *
 * Author: David Petrou
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_getopt.h>
#include <nasd/nasd_threadstuff.h>
#include <nasd/nasd_pdrive.h>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_pdrive_client_kpdev.h>
#include <nasd/nasd_timer.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>

typedef int bool;

bool drive_inited;
nasd_drive_handle_t handle;
nasd_key_t op_key;
nasd_cookie_t op_cookie;
nasd_security_param_t sec_param;
nasd_partnum_t partnum;
nasd_uint16 protection;

int main(int argc, char **argv);
void init_drive(char *server_name, nasd_partnum_t partnum, int binding_type,
        char *master_password);
void cp_dir(char *dir_name);
void cp_file(char *filename);
/*void split_filename(char *filename, char **ext);*/
nasd_identifier_t create_nasd_object(void);
void write_nasd(nasd_identifier_t object_id, nasd_byte_t *buf,
        nasd_offset_t off, nasd_len_t in_len);
void usage(char *progname);
void teardown(void);
void exit_bad(const char *fmt, ...);
void exit_good(void);

int
main(int argc, char **argv)
{
    char *progname;
    char *server_name, *dir_name, *master_password;
    nasd_timer_t timer;
    nasd_timespec_t diff;
    double elapsed_time;
    char c;
    int binding_type;
    
    drive_inited = 0;

    progname = argv[0];

    binding_type = NASD_BIND_DEFAULT;

    while(nasd_getopt(argc, argv, "CMT", &c)) {
      switch(c) {
      case 'C':
        binding_type = NASD_BIND_COLOCATE;
        break;
      case 'M':
        binding_type = NASD_BIND_MSGQ;
        break;
#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE
      case 'T':
        binding_type = NASD_BIND_DCE_DIRECT_TCP;
        break;
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE */
      }
    }

    if(nasd_optind+4 >= argc) {
      usage(progname);
    }

    server_name = argv[nasd_optind++];
    partnum = atoi(argv[nasd_optind++]);
    master_password = argv[nasd_optind++];
    dir_name = argv[nasd_optind];

    nasd_sec_seclevel_to_protection(NASD_MAX_SECURITY_LEVEL, &protection);
    
    init_drive(server_name, partnum, binding_type, master_password);

    NASD_TM_START(&timer);

    cp_dir(dir_name);

    NASD_TM_STOP(&timer);
    NASD_TM_ELAPSED_TS(&timer, &diff);
    elapsed_time = (double)diff.ts_nsec;
    elapsed_time /= (double)NASD_NSEC_PER_SEC;
    elapsed_time += (double)diff.ts_sec;

    fprintf(stderr, "%f seconds\n", elapsed_time);
    
    exit_good();
    /* to shut gcc up */
    exit(0);
}

void
init_drive(char *server_name, nasd_partnum_t partnum, int binding_type,
           char *master_password)
{
    int retval;
    nasd_sec_keyring_t keys;

    assert(server_name);

    nasd_sec_password_to_keys(master_password, partnum, &keys);

    /* we aren't really using capabilities here, just the black daily key */
    bzero((char *)&op_cookie, sizeof(op_cookie));
    sec_param.type = NASD_BLACK_KEY;
    sec_param.partnum = partnum;
    sec_param.actual_protection = NASD_NO_PROTECTION;
    bcopy(keys.black_key, op_cookie.key, sizeof(nasd_key_t));
    bcopy(keys.black_key, op_key, sizeof(nasd_key_t));

    if((retval = nasd_cl_p_init())) {
        exit_bad("ERROR: cannot init client library, rc = %d, line %d\n",
               retval, __LINE__);
    }

    drive_inited = 1;

    retval = nasd_bind_to_drive(server_name, NASD_PDRIVE_PORT, binding_type,
			    NULL, 0, &handle);

    if(retval) {
      exit_bad("ERROR: cannot bind to server %s (line %d) error=0x%x (%s)",
	       server_name, __LINE__, retval, nasd_error_string(retval));
    }
}

void
cp_dir(char *dir_name)
{
    struct dirent *dirp;
    struct stat statbuf;
    DIR *dp;

    assert(dir_name);

    if(chdir(dir_name) == -1) {
        exit_bad("chdir(), line %d", __LINE__);
    }	

    if((dp = opendir(".")) == 0) {
        exit_bad("opendir(), line %d", __LINE__);
    }

    while((dirp = readdir(dp)) != 0) {

	if(stat(dirp->d_name, &statbuf) == -1) {
	    exit_bad("stat(), line %d", __LINE__);
	}

	if(S_ISDIR(statbuf.st_mode)) {
	    continue;
	}

	cp_file(dirp->d_name);
    }
    
    if(closedir(dp) == -1) {
	exit_bad("closedir(), line %d", __LINE__);
    }
}

void
cp_file(char *filename)
{
    int i;
    char buf[BUFSIZ];
    int fd;
/*    char *ext;*/
    nasd_identifier_t object_id;    
    int cur_pos;

    assert(filename);

/*    split_filename(filename, &ext);*/

    if((fd = open(filename, O_RDONLY, 0)) == -1) {
	exit_bad("open(), line %d", __LINE__);
    }
    object_id = create_nasd_object();

    cur_pos = 0;
    while((i = read(fd, buf, BUFSIZ)) != 0) {
	if(i == -1) {
	    exit_bad("read(), line %d", __LINE__);
	}

	write_nasd(object_id, (nasd_byte_t *)buf, cur_pos, i);
	cur_pos += i;
    }

    if(close(fd) == -1) {
	exit_bad("close(), line %d", __LINE__);
    }

    printf("%s\t0x%" NASD_ID_FMT "\n", filename, object_id);
}

/*

at one point i was considering putting the filename extension into the
uninterpreted field in the nasd object.  this code finds the
extension...

void
split_filename(char *filename, char **ext)
{
    int i;

    assert(filename);
    assert(ext && *ext);

    i = 0;
    while(filename[i] && filename[i] != '.') {
	i++;
    }
    if(filename[i] == '.') {
	*ext = filename + i + 1;
    } else {
	*ext = filename + i;
    }
}*/

nasd_identifier_t
create_nasd_object(void)
{
    nasd_p_create_dr_args_t args;    
    nasd_p_create_dr_res_t res;
    nasd_rpc_status_t status;
    nasd_security_param_t sp;
    nasd_error_string_t err_str;
    
    bzero((char *)&args, sizeof(args));
    sp.type = NASD_BLACK_KEY;
    sp.partnum = partnum;
    sp.actual_protection = protection;
    args.in_fieldmask = NASD_ATTR_AV;
    args.in_attribute.av = 7;
    args.in_partnum = partnum;

    nasd_cl_p_create_dr(handle, op_key, &sp, NULL, &args, &res, &status);
    if(status || res.nasd_status) {
	exit_bad("ERROR: status=0x%x (%s) nasd_status=0x%x (%s)",
		 status, nasd_cl_error_string(handle, status, err_str),
		 res.nasd_status, nasd_error_string(res.nasd_status));
    }

    return res.out_identifier;
}

void
write_nasd(nasd_identifier_t object_id, nasd_byte_t *buf, nasd_offset_t off,
	   nasd_len_t in_len)
{
    nasd_p_smpl_op_dr_args_t wr_args;
    nasd_p_fastwrite_dr_res_t wr_res;
    nasd_rpc_status_t status;
    nasd_len_t out_len;
    nasd_status_t rc;
    nasd_error_string_t err_str;

    assert(buf);

    wr_args.in_partnum = partnum;
    wr_args.in_identifier = object_id;
    wr_args.in_offset = off;
    wr_args.in_len = in_len;
    sec_param.partnum = partnum;
    nasd_cl_p_write_simple_dr(handle, op_cookie.key, &sec_param,
			      &op_cookie.capability, &wr_args, buf,
			      &wr_res, &status);
    out_len = wr_res.out_datalen;
    rc = wr_res.nasd_status;

    if(rc || status) {
	exit_bad("WRITE %d at %" NASD_64u_FMT " from 0x%lx ERROR: "
		 "nasd_status=0x%x (%s) status=0x%x (%s)\n",
		 in_len, off, buf, rc, nasd_error_string(rc),
		 status, nasd_cl_error_string(handle, status, err_str));
    }

    if(out_len != in_len) {
	exit_bad("WRITE %d at %" NASD_64u_FMT " from 0x%lx ERROR: "
		 "in_len=%" NASD_64u_FMT " out_len=%" NASD_64u_FMT "\n",
		 in_len, off, buf, in_len, out_len);
    }
}

void
usage(char *progname)
{
    assert(progname);

    exit_bad("USAGE: %s servername partnum master_password directory",
	     progname);
}

void
teardown(void)
{
    if(drive_inited) {
	nasd_cl_p_shutdown();
    }
}

void
exit_bad(const char *fmt, ...)
{
    int errno_save;
    va_list ap;
    static bool here_already = 0;

    /* eliminate recursive calls.  XXX not signal/thread safe.  */
    if(here_already) {
	return;
    }
    here_already = 1;

    errno_save = errno;

    assert(fmt);

    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);

    fprintf(stderr, "\n");

    if(errno_save) {
        fprintf(stderr, "system error: %s\n", strerror(errno_save));
    }

    teardown();
    exit(EXIT_FAILURE);
}

void
exit_good(void)
{
    teardown();
    exit(EXIT_SUCCESS);
}
