// stat a file--build a manifest request, and set its mode // return 0 on success // return -ESTALE if the inode is not local // return -ENOENT if we don't have it // return -ENOMEM on OOM // return -errno on error static int UG_impl_stat( struct SG_gateway* gateway, struct SG_request_data* reqdat, struct SG_request_data* entity_info, mode_t* mode, void* cls ) { int rc = 0; struct UG_state* ug = (struct UG_state*)SG_gateway_cls( gateway ); struct md_entry ent_info; rc = UG_stat_raw( ug, reqdat->fs_path, &ent_info ); if( rc != 0 ) { SG_error("UG_stat_raw('%s') rc = %d\n", reqdat->fs_path, rc ); return rc; } if( ent_info.coordinator != SG_gateway_id( gateway ) ) { // not ours SG_error("Not the coordinator of '%s' (it is now %" PRIu64 ")\n", reqdat->fs_path, ent_info.coordinator ); md_entry_free( &ent_info ); return -ESTALE; } if( mode != NULL ) { *mode = ent_info.mode; } if( entity_info != NULL ) { rc = SG_request_data_init_manifest( gateway, reqdat->fs_path, ent_info.file_id, ent_info.version, ent_info.manifest_mtime_sec, ent_info.manifest_mtime_nsec, entity_info ); if( rc != 0 ) { // OOM md_entry_free( &ent_info ); return -ENOMEM; } if( ent_info.type != MD_ENTRY_FILE ) { // not a file md_entry_free( &ent_info ); return -ENOENT; } } md_entry_free( &ent_info ); return 0; }
void meta_data_destroy(meta_data_t *md) /* {{{ */ { if (md == NULL) return; md_entry_free(md->head); pthread_mutex_destroy(&md->lock); free(md); } /* }}} void meta_data_destroy */
static int md_entry_insert (meta_data_t *md, meta_entry_t *e) /* {{{ */ { meta_entry_t *this; meta_entry_t *prev; if ((md == NULL) || (e == NULL)) return (-EINVAL); pthread_mutex_lock (&md->lock); prev = NULL; this = md->head; while (this != NULL) { if (strcasecmp (e->key, this->key) == 0) break; prev = this; this = this->next; } if (this == NULL) { /* This key does not exist yet. */ if (md->head == NULL) md->head = e; else { assert (prev != NULL); prev->next = e; } e->next = NULL; } else /* (this != NULL) */ { if (prev == NULL) md->head = e; else prev->next = e; e->next = this->next; } pthread_mutex_unlock (&md->lock); if (this != NULL) { this->next = NULL; md_entry_free (this); } return (0); } /* }}} int md_entry_insert */
/* XXX: The lock on md must be held while calling this function! */ static int md_entry_insert_clone(meta_data_t *md, meta_entry_t *orig) /* {{{ */ { meta_entry_t *e; meta_entry_t *this; meta_entry_t *prev; /* WARNINGS : * - we do not check that md and e != NULL here. You should have done it * before. * - we do not use the lock. You should have set it before. */ e = md_entry_clone_contents(orig); prev = NULL; this = md->head; while (this != NULL) { if (strcasecmp(e->key, this->key) == 0) break; prev = this; this = this->next; } if (this == NULL) { /* This key does not exist yet. */ if (md->head == NULL) md->head = e; else { assert(prev != NULL); prev->next = e; } e->next = NULL; } else /* (this != NULL) */ { if (prev == NULL) md->head = e; else prev->next = e; e->next = this->next; } if (this != NULL) { this->next = NULL; md_entry_free(this); } return (0); } /* }}} int md_entry_insert_clone */
// utime int fs_entry_utime( struct fs_core* core, char const* path, struct utimbuf* tb, uint64_t user, uint64_t volume ) { int err = 0; uint64_t parent_id = 0; char* parent_name = NULL; struct fs_entry* fent = fs_entry_resolve_path_and_parent_info( core, path, user, volume, true, &err, &parent_id, &parent_name ); if( !fent || err ) { if( !err ) err = -ENOMEM; return err; } // check permissions if( tb == NULL && !IS_WRITEABLE( fent->mode, fent->owner, fent->volume, user, volume ) ) { fs_entry_unlock( fent ); return -EACCES; } if( tb != NULL && fent->owner != user ) { fs_entry_unlock( fent ); return -EACCES; } if( tb != NULL ) { fent->mtime_sec = tb->modtime; fent->atime = tb->actime; } else { struct timespec ts; clock_gettime( CLOCK_REALTIME, &ts ); fent->mtime_sec = ts.tv_sec; fent->mtime_nsec = ts.tv_nsec; fent->atime = fent->mtime_sec; } fent->atime = currentTimeSeconds(); // post update struct md_entry up; fs_entry_to_md_entry( core, &up, fent, parent_id, parent_name ); int rc = ms_client_update( core->ms, &fent->write_nonce, &up ); if( rc != 0 ) { errorf("ms_client_update(%s) rc = %d\n", path, rc ); } md_entry_free( &up ); fs_entry_unlock( fent ); return rc; }
// do the create remotely on the MS // child must be write-locked int fs_entry_do_MS_create( struct fs_core* core, char const* path, struct fs_entry* child, uint64_t parent_id, char const* parent_name ) { // create this file in the MS struct md_entry data; int rc = 0; fs_entry_to_md_entry( core, &data, child, parent_id, parent_name ); // create synchronously, obtaining the child's file ID and write_nonce rc = ms_client_create( core->ms, &child->file_id, &child->write_nonce, &data ); md_entry_free( &data ); return rc; }
static void md_entry_free(meta_entry_t *e) /* {{{ */ { if (e == NULL) return; free(e->key); if (e->type == MD_TYPE_STRING) free(e->value.mv_string); if (e->next != NULL) md_entry_free(e->next); free(e); } /* }}} void md_entry_free */
/* * Add functions */ int meta_data_add_string(meta_data_t *md, /* {{{ */ const char *key, const char *value) { meta_entry_t *e; if ((md == NULL) || (key == NULL) || (value == NULL)) return (-EINVAL); e = md_entry_alloc(key); if (e == NULL) return (-ENOMEM); e->value.mv_string = md_strdup(value); if (e->value.mv_string == NULL) { ERROR("meta_data_add_string: md_strdup failed."); md_entry_free(e); return (-ENOMEM); } e->type = MD_TYPE_STRING; return (md_entry_insert(md, e)); } /* }}} int meta_data_add_string */
int meta_data_delete (meta_data_t *md, const char *key) /* {{{ */ { meta_entry_t *this; meta_entry_t *prev; if ((md == NULL) || (key == NULL)) return (-EINVAL); pthread_mutex_lock (&md->lock); prev = NULL; this = md->head; while (this != NULL) { if (strcasecmp (key, this->key) == 0) break; prev = this; this = this->next; } if (this == NULL) { pthread_mutex_unlock (&md->lock); return (-ENOENT); } if (prev == NULL) md->head = this->next; else prev->next = this->next; pthread_mutex_unlock (&md->lock); this->next = NULL; md_entry_free (this); return (0); } /* }}} int meta_data_delete */
// chmod int fs_entry_chmod( struct fs_core* core, char const* path, uint64_t user, uint64_t volume, mode_t mode ) { int err = 0; uint64_t parent_id = 0; char* parent_name = NULL; struct fs_entry* fent = fs_entry_resolve_path_and_parent_info( core, path, user, volume, true, &err, &parent_id, &parent_name ); if( !fent || err ) { if( !err ) err = -ENOMEM; return err; } // can't chmod unless we own the file if( fent->owner != user ) { fs_entry_unlock( fent ); free( parent_name ); return -EPERM; } fent->mode = mode; // post update struct md_entry up; fs_entry_to_md_entry( core, &up, fent, parent_id, parent_name ); int rc = ms_client_update( core->ms, &fent->write_nonce, &up ); if( rc != 0 ) { errorf("ms_client_update(%s) rc = %d\n", path, rc ); } md_entry_free( &up ); fs_entry_unlock( fent ); free( parent_name ); return rc; }
// remove a directory, if it is empty int fs_entry_rmdir( struct fs_core* core, char const* path, uint64_t user, uint64_t volume ) { if( core->gateway == GATEWAY_ANON ) { errorf("%s", "Removing directories is forbidden for anonymous gateways\n"); return -EPERM; } // get some info about this directory first int rc = 0; char* fpath = strdup( path ); md_sanitize_path( fpath ); // revalidate this path rc = fs_entry_revalidate_path( core, volume, fpath ); if( rc != 0 && rc != -ENOENT ) { // consistency cannot be guaranteed errorf("fs_entry_revalidate_path(%s) rc = %d\n", fpath, rc ); free( fpath ); return rc; } free( fpath ); int err = 0; struct fs_entry* dent = fs_entry_resolve_path( core, path, user, volume, false, &err ); if( !dent || err ) { return err; } if( dent->ftype != FTYPE_DIR ) { fs_entry_unlock( dent ); return -ENOTDIR; } char* path_dirname = md_dirname( path, NULL ); struct fs_entry* parent = fs_entry_resolve_path( core, path_dirname, user, volume, true, &err ); free( path_dirname ); if( !parent || err ) { fs_entry_unlock( dent ); return err; } // IS THE PARENT EMPTY? if( fs_entry_set_count( dent->children ) > 2 ) { // nope fs_entry_unlock( dent ); fs_entry_unlock( parent ); return -ENOTEMPTY; } struct md_entry ent; fs_entry_to_md_entry( core, &ent, dent, parent->file_id, parent->name ); // tell the MS that this directory should go away rc = ms_client_delete( core->ms, &ent ); md_entry_free( &ent ); if( rc != 0 ) { errorf( "ms_client_delete(%s) rc = %d\n", path, rc ); rc = -EREMOTEIO; fs_entry_unlock( dent ); fs_entry_unlock( parent ); } else { fs_entry_unlock( dent ); // detach from the filesystem rc = fs_entry_detach_lowlevel( core, parent, dent ); if( rc != 0 ) { errorf("fs_entry_detach_lowlevel(%s) rc = %d\n", path, rc ); } fs_entry_unlock( parent ); } return rc; }
// unlink a file from the filesystem // pass -1 if the version is not known, or pass the known version to be unlinked // return -EUCLEAN if we failed to garbage-collect, but needed to (i.e. a manifest was missing) // return -EREMOTEIO for failure to revalidate metadata // return -ESTALE if the given information is out of date int fs_entry_versioned_unlink( struct fs_core* core, char const* path, uint64_t file_id, uint64_t coordinator_id, int64_t known_version, uint64_t owner, uint64_t volume, uint64_t gateway_id, bool check_file_id_and_coordinator_id ) { // can't modify state if anonymous if( core->gateway == SG_GATEWAY_ANON ) { SG_error("%s", "Writing is forbidden for anonymous gateways\n"); return -EPERM; } // get some info about this file first int rc = 0; int err = 0; bool no_manifest = false; // consistency check err = fs_entry_revalidate_path( core, path ); if( err != 0 ) { SG_error("fs_entry_revalidate_path(%s) rc = %d\n", path, err ); if( err == -ENOENT ) return -ENOENT; return -EREMOTEIO; } // look up the parent char* path_dirname = md_dirname( path, NULL ); char* path_basename = md_basename( path, NULL ); struct fs_entry* parent = fs_entry_resolve_path( core, path_dirname, owner, volume, true, &err ); free( path_dirname ); if( !parent || err ) { free( path_basename ); return err; } if( parent->ftype != FTYPE_DIR ) { fs_entry_unlock( parent ); free( path_basename ); return err; } // get the child struct fs_entry* fent = fs_entry_set_find_name( parent->children, path_basename ); free( path_basename ); if( fent == NULL ) { fs_entry_unlock( fent ); fs_entry_unlock( parent ); return -ENOENT; } fs_entry_wlock( fent ); bool local = FS_ENTRY_LOCAL( core, fent ); int64_t version = fent->version; if( check_file_id_and_coordinator_id ) { if( fent->file_id != file_id ) { SG_error("Remote unlink to file %s ID %" PRIX64 ", expected %" PRIX64 "\n", path, file_id, fent->file_id ); fs_entry_unlock( fent ); fs_entry_unlock( parent ); return -ESTALE; } if( fent->coordinator != coordinator_id ) { SG_error("Remote unlink to file %s coordinator %" PRIu64 ", expected %" PRIu64 "\n", path, coordinator_id, fent->coordinator ); fs_entry_unlock( fent ); fs_entry_unlock( parent ); return -ESTALE; } } if( known_version > 0 && fent->version > 0 && fent->version != known_version ) { SG_error("Remote unlink to file %s version %" PRId64 ", expected %" PRId64 "\n", path, known_version, fent->version ); fs_entry_unlock( fent ); fs_entry_unlock( parent ); return -ESTALE; } // make sure the manifest is fresh, so we delete every block // only need to worry about this if file has > 0 size if( fent->size > 0 ) { // try to get it err = fs_entry_revalidate_manifest( core, path, fent ); if( err != 0 ) { SG_error( "fs_entry_revalidate_manifest(%s) rc = %d\n", path, err ); if( err == -ENOENT ) { // continue without a manifest no_manifest = true; SG_error("WARN: no manifest found for %s %" PRIX64 ". Assuming data is already vacuumed.\n", path, fent->file_id ); } else { // some other problem fs_entry_unlock( fent ); fs_entry_unlock( parent ); return err; } } } // tell the driver we're deleting int driver_rc = driver_delete_file( core, core->closure, path, fent ); if( driver_rc != 0 ) { SG_error("driver_delete_file(%s %" PRIX64 ") rc = %d\n", path, fent->file_id, driver_rc ); fs_entry_unlock( fent ); fs_entry_unlock( parent ); return driver_rc; } rc = 0; if( !local ) { // this is someone else's file; tell them to unlink Serialization::WriteMsg* detach_request = new Serialization::WriteMsg(); fs_entry_init_write_message( detach_request, core, Serialization::WriteMsg::DETACH ); fs_entry_prepare_detach_message( detach_request, path, fent, version ); Serialization::WriteMsg* detach_ack = new Serialization::WriteMsg(); // send the write message, or become the coordinator rc = fs_entry_send_write_or_coordinate( core, path, fent, detach_request, detach_ack ); if( rc < 0 ) { SG_error( "fs_entry_send_write_or_coordinate(%s) rc = %d\n", path, rc ); } else if( rc == 0 ) { // successfully sent if( detach_ack->type() != Serialization::WriteMsg::ACCEPTED ) { if( detach_ack->type() == Serialization::WriteMsg::ERROR ) { // could not detach on the remote end SG_error( "remote unlink error = %d (%s)\n", detach_ack->errorcode(), detach_ack->errortxt().c_str() ); rc = detach_ack->errorcode(); } else { // unknown message SG_error( "remote unlink invalid message %d\n", detach_ack->type() ); rc = -EIO; } } } else { // we're now the coordinator. local = true; } delete detach_ack; delete detach_request; } if( local ) { // we're responsible for this file // mark the file as deleted, so it won't show up again in any listing fent->deletion_in_progress = true; // safe to unlock parent--it won't be empty (in a rmdir-able sense) until fent is fully garbage-collected, but fent won't be listed either fs_entry_unlock( parent ); // garbage-collect, then unlink on the MS. Loop this until we succeed in unlinking on the MS (which can only happen // once all of fent's data has been garbage-collected). while( true ) { if( !no_manifest ) { // if we got the latest manifest, garbage-collect all writes on the file rc = fs_entry_vacuumer_file( core, path, fent ); if( rc != 0 ) { SG_error("fs_entry_vacuumer_vacuum_file( %s %" PRIX64 " ) rc = %d\n", path, fent->file_id, rc ); // failed to garbage-collect...need to un-delete fent fent->deletion_in_progress = false; fs_entry_unlock( fent ); return -EREMOTEIO; } } // tell the metadata server we just unlinked // preserve the entry information so we can issue a deletion struct md_entry ent; fs_entry_to_md_entry( core, &ent, fent, parent->file_id, parent->name ); rc = ms_client_delete( core->ms, &ent ); md_entry_free( &ent ); if( rc != 0 ) { SG_error( "ms_client_delete(%s) rc = %d\n", path, rc ); if( rc == -EAGAIN ) { if( !no_manifest ) { // try vacuuming again--some write got added in between our garbage-collection and our unlink request rc = 0; continue; } else { // there are un-garbage-collected writes, but we have no manifest, so we can't vacuum in order to proceed with the delete. SG_error("MEMORY LEAK DETECTED: No manifest for %" PRIX64 " available; unable to vacuum!\n", fent->file_id ); // failed to garbage-collect...need to un-delete fent fent->deletion_in_progress = false; fs_entry_unlock( fent ); return -EUCLEAN; } } else { // something more serious rc = -EREMOTEIO; fent->deletion_in_progress = false; fs_entry_unlock( fent ); return rc; } } else { // success! break; } } // re-lock the parent--it's guaranteed to exist, since it's not empty fs_entry_wlock( parent ); // unlock fent--we're done with it fs_entry_unlock( fent ); // detatch fent from parent rc = fs_entry_detach_lowlevel( core, parent, fent ); if( rc != 0 ) { SG_error("fs_entry_detach_lowlevel(%" PRIX64 ") rc = %d\n", fent->file_id, rc ); fs_entry_unlock( parent ); return rc; } fs_entry_unlock( parent ); } return rc; }
// make a node (regular files only at this time) int fs_entry_mknod( struct fs_core* core, char const* path, mode_t mode, dev_t dev, uint64_t user, uint64_t vol ) { // only regular files at this time... if( ! ( S_ISREG( mode ) || S_ISFIFO( mode ) ) ) { return -ENOTSUP; } // revalidate this path int rc = fs_entry_revalidate_path( core, path ); if( rc != 0 && rc != -ENOENT ) { // consistency cannot be guaranteed SG_error("fs_entry_revalidate_path(%s) rc = %d\n", path, rc ); return rc; } int err = 0; // get the parent directory and lock it char* path_dirname = md_dirname( path, NULL ); struct fs_entry* parent = fs_entry_resolve_path( core, path_dirname, user, vol, true, &err ); free( path_dirname ); if( !IS_DIR_READABLE( parent->mode, parent->owner, parent->volume, user, vol ) ) { // not searchable fs_entry_unlock( parent ); return -EACCES; } if( !IS_WRITEABLE( parent->mode, parent->owner, parent->volume, user, vol ) ) { // not writeable fs_entry_unlock( parent ); return -EACCES; } uint64_t parent_id = parent->file_id; char* parent_name = strdup( parent->name ); char* path_basename = md_basename( path, NULL ); // make sure it doesn't exist already (or isn't in the process of being deleted, since we might have to re-create it if deleting it fails) if( fs_entry_set_find_name( parent->children, path_basename ) != NULL ) { free( path_basename ); fs_entry_unlock( parent ); free( parent_name ); return -EEXIST; } struct fs_entry* child = (struct fs_entry*)calloc( sizeof(struct fs_entry), 1 ); struct timespec ts; clock_gettime( CLOCK_REALTIME, &ts ); int mmode = 0; if (S_ISFIFO(mode)) { mmode = ( mode & 0777 ) | S_IFIFO; err = fs_entry_init_fifo( core, child, path_basename, 0, fs_entry_next_file_version(), user, core->gateway, vol, mmode, 0, ts.tv_sec, ts.tv_nsec, 0, 0 ); } if (S_ISREG(mode)) { mmode = ( mode & 0777 ); err = fs_entry_init_file( core, child, path_basename, 0, fs_entry_next_file_version(), user, core->gateway, vol, mmode, 0, ts.tv_sec, ts.tv_nsec, 0, 0 ); } if( err == 0 ) { // mark it as created in this session child->created_in_session = true; // we're creating, so this manifest is initialized (to zero blocks) child->manifest->initialize_empty( child->version ); fs_entry_wlock( child ); // call the driver err = driver_create_file( core, core->closure, path, child ); if( err != 0 ) { // undo SG_error("driver_create_file(%s) rc = %d\n", path, err ); child->open_count = 0; fs_entry_unlock( child ); fs_entry_destroy( child, false ); free( child ); } else { // attach the file fs_entry_attach_lowlevel( core, parent, child ); struct md_entry data; fs_entry_to_md_entry( core, &data, child, parent_id, parent_name ); // create the child on the MS, obtaining its file ID and write nonce err = ms_client_create( core->ms, &child->file_id, &child->write_nonce, &data ); md_entry_free( &data ); if( err != 0 ) { SG_error( "ms_client_create(%s) rc = %d\n", path, err ); err = -EREMOTEIO; child->open_count = 0; fs_entry_unlock( child ); fs_entry_detach_lowlevel( core, parent, child ); free( child ); } else { fs_entry_unlock( child ); } } } fs_entry_unlock( parent ); free( parent_name ); free( path_basename ); return err; }
// finish up getting directory metadata, and free up the download handle // return 0 on success, and set *batch_id to this download's batch // *ret_num_children to the number of children downloaded, and *max_gen to be the largest generation number seen. // return -ENOMEM on OOM static int ms_client_get_dir_metadata_end( struct ms_client* client, uint64_t parent_id, struct md_download_context* dlctx, ms_client_dir_listing* dir_listing, int64_t* batch_id, size_t* ret_num_children, int64_t* max_gen ) { int rc = 0; int listing_error = 0; struct md_entry* children = NULL; size_t num_children = 0; size_t num_unique_children = 0; CURL* curl = NULL; int64_t biggest_generation = 0; struct ms_client_get_dir_download_state* dlstate = (struct ms_client_get_dir_download_state*)md_download_context_get_cls( dlctx ); md_download_context_set_cls( dlctx, NULL ); // download status? rc = ms_client_download_parse_errors( dlctx ); if( rc != 0 ) { if( rc != -EAGAIN) { // fatal SG_error("ms_client_download_parse_errors( %p ) rc = %d\n", dlctx, rc ); } md_download_context_unref_free( dlctx, &curl ); if( curl != NULL ) { curl_easy_cleanup( curl ); } ms_client_get_dir_download_state_free( dlstate ); dlstate = NULL; return rc; } // collect the data rc = ms_client_listing_read_entries( client, dlctx, &children, &num_children, &listing_error ); // done with the download md_download_context_unref_free( dlctx, &curl ); if( curl != NULL ) { curl_easy_cleanup( curl ); } ms_client_get_dir_download_state_free( dlstate ); dlstate = NULL; // did we get valid data? if( rc != 0 ) { SG_error("ms_client_listing_read_entries(%p) rc = %d\n", dlctx, rc ); return rc; } if( listing_error != MS_LISTING_NEW ) { // somehow we didn't get data. shouldn't happen in listdir SG_error("BUG: failed to get listing data for %" PRIX64 ", listing_error = %d\n", parent_id, listing_error ); return -ENODATA; } // merge children in for( unsigned int i = 0; i < num_children; i++ ) { uint64_t file_id = children[i].file_id; SG_debug("%p: %" PRIX64 "\n", dlctx, file_id ); if( dir_listing->count( file_id ) > 0 ) { SG_warn("Duplicate child %" PRIX64 "\n", file_id ); md_entry_free( &children[i] ); continue; } try { (*dir_listing)[ file_id ] = children[i]; } catch( bad_alloc& ba ) { rc = -ENOMEM; break; } // generation? if( children[i].generation > biggest_generation ) { biggest_generation = children[i].generation; } num_unique_children ++; } // NOTE: shallow free--we've copied the children into dir_listing SG_safe_free( children ); *ret_num_children = num_unique_children; *max_gen = biggest_generation; return 0; }
// entry point int main( int argc, char** argv ) { int rc = 0; struct UG_state* ug = NULL; struct SG_gateway* gateway = NULL; int path_optind = 0; struct tool_opts opts; struct md_entry ent_data; char* path = NULL; uint64_t new_coord = 0; memset( &opts, 0, sizeof(tool_opts) ); rc = parse_args( argc, argv, &opts ); if( rc != 0 ) { usage( argv[0], "file [file...]" ); md_common_usage(); exit(1); } // setup... ug = UG_init( argc, argv, opts.anonymous ); if( ug == NULL ) { SG_error("%s", "UG_init failed\n" ); exit(1); } gateway = UG_state_gateway( ug ); // get the list of files to coordinate path_optind = SG_gateway_first_arg_optind( gateway ); if( path_optind == argc ) { usage( argv[0], "file [file...]" ); md_common_usage(); UG_shutdown( ug ); exit(1); } for( int i = path_optind; i < argc; i++ ) { path = argv[i]; // make sure this is a file... rc = UG_stat_raw( ug, path, &ent_data ); if( rc != 0 ) { SG_error("UG_stat_raw('%s') rc = %d\n", path, rc ); rc = 1; goto UG_coord_shutdown; } if( ent_data.type != MD_ENTRY_FILE ) { fprintf(stderr, "Not a file: %s\n", path ); rc = 1; goto UG_coord_shutdown; } // if we're not the coordinator, become it if( ent_data.coordinator != SG_gateway_id( gateway ) ) { SG_debug("Become the coordinator of '%s'\n", path ); rc = UG_chcoord( ug, path, &new_coord ); if( rc != 0 ) { fprintf(stderr, "chcoord '%s': %s\n", path, strerror(-rc) ); rc = 1; goto UG_coord_shutdown; } } md_entry_free( &ent_data ); } // proceed to handle requests SG_debug("%s", "Proceed to handle requests\n"); rc = UG_main( ug ); if( rc != 0 ) { fprintf(stderr, "UG_main: %s\n", strerror(-rc) ); rc = 1; } UG_coord_shutdown: UG_shutdown( ug ); exit(rc); }