/** * Create a volume in a given group * * @param[in] params The parsed command array * * The real parameters passed in the array are: * - UUID of the group in which the volume has to be created * - Name of the volume to create * - UUID of the volume to create * - Size of the volume to create (in KB) * * @return 0 on success, a negative error code on failure */ static int vrt_cmd_volume_create(const struct VrtVolumeCreate *cmd) { vrt_group_t *group; vrt_volume_t *volume; int ret; EXA_ASSERT(cmd->volume_size > 0); exalog_debug("create volume '%s': size %" PRIu64 " KB", cmd->volume_name, cmd->volume_size); group = vrt_get_group_from_uuid(&cmd->group_uuid); if (group == NULL) { exalog_debug("Unknown group " UUID_FMT, UUID_VAL(&cmd->group_uuid)); return -VRT_ERR_UNKNOWN_GROUP_UUID; } /* !!! All sizes in 'cmd' are in KB and VRT internal functions want sizes in * sectors. */ ret = vrt_group_create_volume(group, &volume, &cmd->volume_uuid, cmd->volume_name, KBYTES_2_SECTORS(cmd->volume_size)); if (ret != EXA_SUCCESS) { exalog_error("Can't create volume '%s' in group '%s': %s(%d)", cmd->volume_name, group->name, exa_error_msg(ret), ret); vrt_group_unref(group); return ret; } EXA_ASSERT(volume != NULL); /* wipe the newly created volume * * FIXME: This code is called from all the clients while it should be done * only once. To do so we should add a new RPC and trigger the wipping from * admind. */ /* Let only one node (the first one) do the wipe */ if (vrt_node_get_upnode_id() == 0) { ret = vrt_group_wipe_volume(group, volume); if (ret != EXA_SUCCESS) { exalog_error("Can't wipe volume '%s' in group '%s': %s(%d)", volume->name, group->name, exa_error_msg(ret), ret); /* Rollback volume creation */ vrt_group_delete_volume(group, volume); vrt_group_unref(group); return ret; } } vrt_group_unref(group); return EXA_SUCCESS; }
static void write_result(int msg_sock, const char *result) { int skip = 0; if (result == NULL || msg_sock < 0) return; do { int rv; do rv = os_send(msg_sock, result + skip, strlen(result) - skip); while (rv == -EINTR); if (rv < 0 && rv != -EAGAIN) { exalog_error("Error '%s' while sending result '%s'", exa_error_msg(rv), result); break; } if (rv > 0) skip += rv; } while (skip < strlen(result)); }
static void local_exa_fscreate(int thr_nb, void *msg) { int ret = EXA_SUCCESS; struct fscreate_info *info = msg; const fs_definition_t *fs_definition; if (info->nodeid != adm_my_id) { ret = -ADMIND_ERR_NOTHINGTODO; goto local_exa_fscreate_end; } fs_definition = fs_get_definition(info->fs.fstype); if (!fs_definition) { ret = -EXA_ERR_INVALID_PARAM; goto local_exa_fscreate_end; } EXA_ASSERT(fs_definition->create_fs); ret = fs_definition->create_fs(thr_nb, &info->fs); local_exa_fscreate_end: exalog_debug("local_exa_fscreate() = %s", exa_error_msg(ret)); admwrk_ack(thr_nb, ret); }
/** * Receive incoming pings through Examsg. * * \param[out] ping Received ping * * \return true if a ping was received, false otherwise */ bool sup_recv_ping(sup_ping_t *ping) { /* initialized to 0s, this will trigger a ping at the very beginning */ static struct timeval timeout = { 0, 0 }; ExamsgMID mid; sup_ping_msg_t msg; int err; err = examsgWaitTimeout(sup_mh, &timeout); if (err < 0 && err != -ETIME) { __error("encountered an error while waiting for messages : %s (%d)", exa_error_msg(err), err); return false; } if (err == -ETIME) { do_ping = true; timeout.tv_sec = ping_period; timeout.tv_usec = 0; return false; } err = examsgRecv(sup_mh, &mid, &msg, sizeof(msg)); /* XXX shouldn't happen (?) because of examsgWaitTimeout() above, * but it *does* happen at clstop */ if (err == 0) return false; if (err < 0) { __error("encountered an error while retrieving message : %s (%d)", exa_error_msg(err), err); return false; } EXA_ASSERT(err == sizeof(msg)); EXA_ASSERT(msg.any.type == EXAMSG_SUP_PING); *ping = msg.ping; return true; }
/** select a free connection * * \param[in] sock_xml: socket on which it receives commands. */ static void handle_connection(int msg_sock) { int i; static cl_error_desc_t busy = { .code = -EXA_ERR_ADM_BUSY, .msg = "Too many connections."}; /* Search free slot in connection array */ for (i = 0; i < MAX_CONNECTION; i++) { if (connectlist[i].uid == CMD_UID_INVALID && connectlist[i].fd == -1) { exalog_debug("CONNECTION %d: Using sock %d", i, msg_sock); /* uid is set when the command is actually scheduled */ connectlist[i].fd = msg_sock; FD_SET(msg_sock, &setSocks); return; } } /* * No free connection found. We disconnect the caller now because we * are no room in our connectionlist */ exalog_error("All connections are busy sock=%d", msg_sock); cli_command_end_complete(msg_sock, &busy); os_closesocket(msg_sock); } /*-------------------------------------------------------------------------*/ /** \brief Connection thread: wait on xml message to pass the command * to the work thread. * * \param[in] sock_xml_proto: socket xml on which it receives commands. * \return the selected working thread or a negative error code * */ /*-------------------------------------------------------------------------*/ static void accept_new_client(void) { int fd = os_accept(listen_fd, NULL, NULL); exalog_debug("Got an incoming XML Request on socket: %d", fd); if (fd < 0) { exalog_error("Failed during admind xml connection accept: error=%s", exa_error_msg(-fd)); return; } handle_connection(fd); }
/** * Read and process an event from the network mailbox. * * \param[in,out] pi Ping info * * \return 1 if processed an event, 0 if not, and negative error code otherwise */ static int net_mbox_event(ping_info_t *pi) { ExamsgMID mid; static char msg[sizeof(ExamsgNetRqst) + sizeof(Examsg)]; int s; if (network_status() != 0) return 0; s = examsgMboxRecv(EXAMSG_NETMBOX_ID, &mid, sizeof(mid), msg, sizeof(msg)); if (s == 0) /* Nothing to read */ return 0; if (s <= sizeof(ExamsgNetRqst)) { exalog_error("received %d bytes, error %s", s, exa_error_msg(s)); return -EINVAL; } s = network_send(&mid, (ExamsgNetRqst *)msg); if (s < 0) { if (network_manageable(s)) { remember_unsent(&mid, msg, sizeof(msg)); return s; } exalog_error("net mailbox event: error %s", exa_error_msg(s)); return -EIO; } reset_ping(pi); return 1; }
/** \brief Implements the get_cluster_name command * * Just return the current cluster name */ static void cluster_get_cluster_name(int thr_nb, void *dummy, cl_error_desc_t *err_desc) { if (!adm_cluster.created) { set_error(err_desc, -EXA_ERR_ADMIND_NOCONFIG, exa_error_msg(-EXA_ERR_ADMIND_NOCONFIG)); return; } send_payload_str(adm_cluster.name); set_success(err_desc); }
static int vrt_cmd_group_resync(const struct vrt_group_resync_request *cmd) { struct vrt_group *group; int err; group = vrt_get_group_from_uuid(&cmd->group_uuid); if (group == NULL) return -VRT_ERR_UNKNOWN_GROUP_UUID; err = vrt_group_resync(group, &cmd->nodes); if (err != EXA_SUCCESS) exalog_error("Resync failed: %s (%d)", exa_error_msg(err), err); vrt_group_unref(group); return err; }
/** * Send a ping to all instances of Csupd using examsg. * * \param[in] cluster Cluster * \param[in] view View to send */ void sup_send_ping(const sup_cluster_t *cluster, const sup_view_t *view) { sup_ping_msg_t ping_msg; int err; ping_msg.any.type = EXAMSG_SUP_PING; ping_msg.ping.sender = cluster->self->id; ping_msg.ping.incarnation = cluster->self->incarnation; sup_view_copy(&ping_msg.ping.view, view); EXA_ASSERT(sup_check_ping(&ping_msg.ping, 'S')); err = examsgSendNoBlock(sup_mh, EXAMSG_CMSGD_ID, EXAMSG_LOCALHOST, &ping_msg, sizeof(ping_msg)); if (err != sizeof(ping_msg)) __debug("cannot send ping : %s (%d)", exa_error_msg(err), err); }
/** * Set up the messaging. * * \param[in] local_id Local node's id * * \return true if successfull, false otherwise */ bool sup_setup_messaging(exa_nodeid_t local_id) { int err; sup_mh = examsgInit(EXAMSG_CSUPD_ID); if (!sup_mh) return false; /* Create local mailbox, buffer at most SUP_MAX_PING_MSG ping messages */ err = examsgAddMbox(sup_mh, EXAMSG_CSUPD_ID, SUP_MAX_PING_MSG, sizeof(sup_ping_msg_t)); if (err) { __error("cannot create mailbox : %s (%d)", exa_error_msg(err), err); if (sup_mh) examsgExit(sup_mh); return false; } return true; }
static const char *status_info_str(rdev_req_status_t status) { return status < 0 ? exa_error_msg(status) : exa_rdev_status_name(status); }
int main(int argc, char *argv[]) { bool static_init_ok = false; const char *action; int err = 0; self = os_program_name(argv[0]); if (argc <= 1) { usage(); err = 1; goto done; } err = examsg_static_init(EXAMSG_STATIC_GET); if (err != 0) { fprintf(stderr, "examsg_static_init failed: %s (%d)\n", exa_error_msg(-err), err); goto done; } static_init_ok = true; mh = examsgInit(EXAMSG_TEST_ID); if (!mh) { fprintf(stderr, "examsgInit failed\n"); goto done; } err = examsgAddMbox(mh, examsgOwner(mh), 3, EXAMSG_MSG_MAX); if (err != 0) { fprintf(stderr, "examsgAddMbox failed: %s (%d)\n", exa_error_msg(-err), err); goto done; } action = argv[1]; if (strcmp(action, "is_fs_mounted") == 0) { int m; if (argc != 3) { usage(); goto done; } m = fsd_is_fs_mounted(mh, argv[2]); if (m < 0) err = m; } else if (strcmp(action, "is_mountpoint_used") == 0) { int u; if (argc != 3) { usage(); goto done; } u = fsd_is_mountpoint_used(mh, argv[2]); if (u < 0) err = u; } else if (strcmp(action, "prepare_gfs") == 0) { fs_data_t fs; size_t sz; if (argc != 3) { usage(); goto done; } COMPILE_TIME_ASSERT(sizeof("sfs") <= sizeof(fs.fstype)); os_strlcpy(fs.fstype, "sfs", sizeof(fs.fstype)); sz = os_strlcpy(fs.clustered.gfs.lock_protocol, argv[2], sizeof(fs.clustered.gfs.lock_protocol)); if (sz >= sizeof(fs.clustered.gfs.lock_protocol)) { fprintf(stderr, "Invalid lock protocol: '%s' (too long)\n", argv[2]); goto done; } err = fsd_prepare(mh, &fs); } else if (strcmp(action, "mount") == 0) { fs_data_t fs; size_t sz; if (argc != 7) { usage(); goto done; } sz = os_strlcpy(fs.fstype, argv[2], sizeof(fs.fstype)); if (sz >= sizeof(fs.fstype)) { fprintf(stderr, "Invalid fs type: '%s' (too long)\n", argv[2]); goto done; } sz = os_strlcpy(fs.mountpoint, argv[3], sizeof(fs.mountpoint)); if (sz >= sizeof(fs.mountpoint)) { fprintf(stderr, "Invalid mountpoint: '%s' (too long)\n", argv[3]); goto done; } sz = os_strlcpy(fs.devpath, argv[4], sizeof(fs.devpath)); if (sz >= sizeof(fs.devpath)) { fprintf(stderr, "Invalid dev path: '%s' (too long)\n", argv[4]); goto done; } err = fsd_mount(mh, &fs, 1 , 0, argv[5], argv[6]); } else if (strcmp(action, "umount") == 0) { fs_data_t fs; size_t sz; if (argc != 6) { usage(); goto done; } sz = os_strlcpy(fs.mountpoint, argv[2], sizeof(fs.mountpoint)); if (sz >= sizeof(fs.mountpoint)) { fprintf(stderr, "Invalid mountpoint: '%s' (too long)\n", argv[2]); goto done; } sz = os_strlcpy(fs.devpath, argv[3], sizeof(fs.devpath)); if (sz >= sizeof(fs.devpath)) { fprintf(stderr, "Invalid dev path: '%s' (too long)\n", argv[3]); goto done; } err = fsd_umount(mh, &fs, argv[5], argv[6]); } else if (strcmp(action, "unload") == 0) { fs_data_t fs; size_t sz; if (argc != 3) { usage(); goto done; } sz = os_strlcpy(fs.fstype, argv[2], sizeof(fs.fstype)); if (sz >= sizeof(fs.fstype)) { fprintf(stderr, "Invalid fs type: '%s' (too long)\n", argv[2]); goto done; } err = fsd_unload(mh, &fs); } else if (strcmp(action, "create_local") == 0) { if (argc != 4) { usage(); goto done; } err = fsd_fs_create_local(mh, argv[2], argv[3]); } else if (strcmp(action, "create_gfs") == 0) { fs_data_t fs; size_t sz; if (argc != 7) { usage(); goto done; } COMPILE_TIME_ASSERT(sizeof("sfs") <= sizeof(fs.fstype)) sz = os_strlcpy(fs.fstype, "sfs", sizeof(fs.fstype)); sz = os_strlcpy(fs.devpath, argv[2], sizeof(fs.devpath)); if (sz >= sizeof(fs.devpath)) { fprintf(stderr, "Invalid dev path: '%s' (too long)\n", argv[2]); goto done; } sz = os_strlcpy(fs.clustered.gfs.lock_protocol, argv[3], sizeof(fs.clustered.gfs.lock_protocol)); if (sz >= sizeof(fs.clustered.gfs.lock_protocol)) { fprintf(stderr, "Invalid lock protocol: '%s' (too long)\n", argv[3]); goto done; } if (to_uint64(argv[4], &fs.sizeKB) != EXA_SUCCESS) { fprintf(stderr, "Invalid size: '%s'\n", argv[4]); goto done; } sz = os_strlcpy(fs.clustered.gfs.uuid, argv[5], sizeof(fs.clustered.gfs.uuid)); if (sz >= sizeof(fs.clustered.gfs.uuid)) { fprintf(stderr, "Invalid GFS uuid: '%s' (too long)\n", argv[5]); goto done; } if (to_uint64(argv[6], &fs.clustered.gfs.nb_logs) != EXA_SUCCESS) { fprintf(stderr, "Invalid number of logs: '%s'\n", argv[6]); goto done; } err = fsd_fs_create_gfs(mh, &fs); } else if (strcmp(action, "dfinfo") == 0) { struct fsd_capa buf; if (argc != 3) { usage(); goto done; } err = fsd_df(mh, argv[2], &buf); if (err == 0) printf("size=%"PRId64" bytes\n" "used=%"PRId64" bytes\n" "free=%"PRId64" bytes\n", buf.size, buf.used, buf.free); } else if (strcmp(action, "resize") == 0) { uint64_t size_kb; if (argc != 6) { usage(); goto done; } if (to_uint64(argv[5], &size_kb) != EXA_SUCCESS) { fprintf(stderr, "Invalid new size: '%s'\n", argv[5]); goto done; } err = fsd_resize(mh, argv[2], argv[3], argv[4], size_kb); } else if (strcmp(action, "prepare_resize") == 0) { if (argc != 4) { usage(); goto done; } err = fsd_prepare_resize(mh, argv[2], argv[3]); } else if (strcmp(action, "read_shm") == 0) { read_shm(); } else if (strcmp(action, "add_logs") == 0) { fs_data_t fs; int num_logs, actual_num_logs; size_t sz; if (argc != 4) { usage(); goto done; } COMPILE_TIME_ASSERT(sizeof("sfs") <= sizeof(fs.fstype)); os_strlcpy(fs.fstype, "sfs", sizeof(fs.fstype)); sz = os_strlcpy(fs.devpath, argv[2], sizeof(fs.devpath)); if (sz >= sizeof(fs.devpath)) { fprintf(stderr, "Invalid dev path: '%s' (too long)\n", argv[2]); goto done; } if (to_int(argv[3], &num_logs) != EXA_SUCCESS) { fprintf(stderr, "Invalid number of logs: '%s'\n", argv[3]); goto done; } actual_num_logs = fsd_set_gfs_logs(mh, &fs, num_logs); if (actual_num_logs < 0) err = actual_num_logs; else { examsgDelMbox(mh, EXAMSG_TEST_ID); printf("Number of logs after the operation: %d\n", actual_num_logs); } } else usage(); if (err != 0) fprintf(stderr, "Action finished with error %d: %s\n", err, exa_error_msg(-err)); done: examsgDelMbox(mh, EXAMSG_TEST_ID); if (mh != NULL) examsgExit(mh); if (static_init_ok) examsg_static_clean(EXAMSG_STATIC_RELEASE); return err == 0 ? 0 : 1; }
/** \brief Implements the fscreate command * * - Set nodes started and stopped. * - Set nodes mounted and unmounted. * - Add it to the config tree with status NOK * - This command runs the FS-specific check and create command: creation * of new volumes is done through specific FS function * - Update status to OK in the config file. */ static void cluster_fscreate(int thr_nb, void *data, cl_error_desc_t *err_desc) { struct fscreate_params *fs_param = data; int error_val = EXA_SUCCESS, error_delete; struct adm_volume *volume; struct adm_group *group; fs_data_t new_fs; const fs_definition_t *fs_definition; struct vrt_volume_info volume_info; exalog_info("received fscreate '%s:%s' " "--mountpoint='%s' --type='%s' --size=%" PRIu64 " --rg-size=%" PRIu64, fs_param->group_name, fs_param->volume_name, fs_param->mountpoint, fs_param->type, fs_param->sizeKB, fs_param->rg_sizeM); /* Check the license status to send warnings/errors */ cmd_check_license_status(); /* This is a workaround for bug #4600. */ if (fs_param->sizeKB % 4 != 0) { set_error(err_desc, -EXA_ERR_INVALID_PARAM, "Size must be a multiple of 4KiB"); return; } group = adm_group_get_group_by_name(fs_param->group_name); if (group == NULL) { set_error(err_desc, -ADMIND_ERR_UNKNOWN_GROUPNAME, NULL, fs_param->group_name); return; } /* The volume MUST NOT exist */ if (adm_cluster_get_volume_by_name(group->name, fs_param->volume_name)) { set_error(err_desc, -EXA_ERR_INVALID_PARAM, "A file system or a volume with this name already exists"); return; } fs_definition = fs_get_definition(fs_param->type); if (!fs_definition) { set_error(err_desc, -EXA_ERR_INVALID_PARAM, "Unknown file system type"); return; } /* FIXME use cluster_vlcreate in place of vrt_master_volume_create */ error_val = vrt_master_volume_create(thr_nb, group, fs_param->volume_name, EXPORT_BDEV, fs_param->sizeKB, fs_definition->is_volume_private(), adm_cluster_get_param_int("default_readahead")); if (error_val != EXA_SUCCESS) goto volume_delete; /* get the newly create volume */ volume = adm_group_get_volume_by_name(group, fs_param->volume_name); /* Ask the vrt for the real size because "-s max" uses a size of "0" */ if ((error_val = vrt_client_volume_info(adm_wt_get_localmb(), &group->uuid, &volume->uuid, &volume_info)) != EXA_SUCCESS) { goto volume_delete; } /* fill the structure with informations retrieved from the vrt */ memset(&new_fs, 0, sizeof(new_fs)); strlcpy(new_fs.fstype, fs_param->type, sizeof(new_fs.fstype)); strlcpy(new_fs.mountpoint, fs_param->mountpoint, sizeof(new_fs.mountpoint)); new_fs.sizeKB = volume_info.size; new_fs.volume_uuid = volume->uuid; new_fs.transaction = 0; adm_volume_path(new_fs.devpath, sizeof(new_fs.devpath), group->name, volume->name); /* Parse the filesystem-specific command parameters */ if (fs_definition->parse_fscreate_parameters) { error_val = fs_definition->parse_fscreate_parameters(&new_fs, fs_param->sfs_nb_logs, fs_param->rg_sizeM); if (error_val != EXA_SUCCESS) goto volume_delete; } /* Write to tree, with INPROGRESS status */ error_val = fs_update_tree(thr_nb, &new_fs); if (error_val != EXA_SUCCESS) goto volume_delete; exalog_debug("Set FS tree to NOK successfully"); /* Perform clustered preparation specific to fs type */ if (fs_definition->pre_create_fs) { error_val = fs_definition->pre_create_fs(thr_nb, &new_fs); if (error_val != EXA_SUCCESS) goto volume_delete; } /* Really create */ error_val = pre_local_create_fs(thr_nb, &new_fs); if (error_val != EXA_SUCCESS) goto volume_delete; exalog_debug("Created FS successfully"); /* Set transaction to COMMITTED */ new_fs.transaction = 1; error_val = fs_update_tree(thr_nb, &new_fs); if (error_val != EXA_SUCCESS) goto volume_delete; exalog_debug("Set FS tree to COMMITTED successfully"); set_success(err_desc); return; volume_delete: /* retrieve the volume the was supposed to be created */ volume = adm_group_get_volume_by_name(group, fs_param->volume_name); if (error_val == -ADMIND_ERR_NODE_DOWN || error_val == -ADMIND_ERR_METADATA_CORRUPTION || !volume) { set_error(err_desc, error_val, NULL); return; } /* Try to remove the volume in the config file. If this fails * for any reason, silently ignore the error: the FS is rolled * back to an invalid status and cannot be started. The only way * out is to fsdelete it. */ error_delete = vrt_master_volume_delete(thr_nb, volume, false); if (error_delete != EXA_SUCCESS) { exalog_error("Cannot delete file system: %s", exa_error_msg(error_delete)); } set_error(err_desc, error_val, NULL); return; }
static void check_internal_msg(void) { struct timeval timeout = { .tv_sec = 0, .tv_usec = EXAMSG_TIMEOUT }; static Examsg msg; command_end_t *end; int i, ret; ret = examsgWaitTimeout(cli_mh, &timeout); if (ret < 0 && ret != -ETIME) { exalog_error("Message wait failed %s (%d)", exa_error_msg(ret), ret); return; } if (ret == -ETIME) return; ret = examsgRecv(cli_mh, NULL, &msg, sizeof(msg)); if (ret == 0) return; EXA_ASSERT_VERBOSE(ret > 0, "Message receive failed: %s (%d)", exa_error_msg(ret), ret); if (ret < 0) exalog_error("Message receive failed: %s (%d)", exa_error_msg(ret), ret); /* The CLI server can only receive EXAMSG_ADM_CLUSTER_CMD_END messages for now */ EXA_ASSERT(msg.any.type == EXAMSG_ADM_CLUSTER_CMD_END); end = (command_end_t *)msg.payload; for (i = 0; i < MAX_CONNECTION; i++) if (end->cuid == connectlist[i].uid) { cli_command_end_complete(connectlist[i].fd, &end->err_desc); connectlist[i].uid = CMD_UID_INVALID; break; } EXA_ASSERT(i < MAX_CONNECTION); } static void check_tcp_connection(void) { static struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 }; fd_set setSave = setSocks; int ret, conn_id; do ret = os_select(FD_SETSIZE, &setSave, NULL, NULL, &timeout); while (ret == -EINTR); if (ret < 0) { /* FIXME should assert ? */ exalog_debug("Select failed %m"); return; } /* Check working sockets */ for (conn_id = 0; conn_id < MAX_CONNECTION; ++conn_id) { int sock_fd = connectlist[conn_id].fd; if (sock_fd >= 0 && FD_ISSET(sock_fd, &setSave)) handle_inputdata(conn_id, sock_fd); } /* Must be done at the end to make sure messages for current * working threads are processed first */ if (FD_ISSET(listen_fd, &setSave)) accept_new_client(); } /*-------------------------------------------------------------------------*/ /** \brief Connection thread: wait on xml message and pass the command * to the work thread. * * \param[in] sock_xml: socket xml on which it receives commands. * */ /*-------------------------------------------------------------------------*/ static void cli_server(void *data) { int i; /* Initialize exalog */ exalog_as(EXAMSG_ADMIND_ID); exalog_debug("cli_server: started"); /* Initialization */ FD_ZERO(&setSocks); FD_SET(listen_fd, &setSocks); for (i = 0; i < MAX_CONNECTION; i++) { connectlist[i].fd = -1; /* A command cannot be CMD_UID_INVALID, so CMD_UID_INVALID means here * no command running */ connectlist[i].uid = CMD_UID_INVALID; } while (!stop) { check_tcp_connection(); check_internal_msg(); } os_closesocket(listen_fd); os_net_cleanup(); examsgDelMbox(cli_mh, EXAMSG_ADMIND_CLISERVER_ID); examsgExit(cli_mh); } int cli_server_start(void) { listen_fd = listen_socket_port(ADMIND_SOCKET_PORT); if (listen_fd < 0) return listen_fd; cli_mh = examsgInit(EXAMSG_ADMIND_CLISERVER_ID); if (!cli_mh) return -EINVAL; /* The mailbox needs to be able to receive command end messages from the * event manager; as there can be at most MAX_CONNECTION client connections * we can receive at the time at most 10 command end messages. */ examsgAddMbox(cli_mh, EXAMSG_ADMIND_CLISERVER_ID, MAX_CONNECTION, sizeof(command_end_t)); stop = false; if (!exathread_create_named(&thr_xml_proto, ADMIND_THREAD_STACK_SIZE+MIN_THREAD_STACK_SIZE, &cli_server, NULL, "exa_adm_xml")) return -EXA_ERR_DEFAULT; return EXA_SUCCESS; }
void rebuild_helper_thread(void *p) { ExamsgHandle mh; int err; exalog_as(EXAMSG_NBD_SERVER_ID); /* initialize examsg framework */ mh = examsgInit(EXAMSG_NBD_LOCKING_ID); EXA_ASSERT(mh != NULL); err = examsgAddMbox(mh, EXAMSG_NBD_LOCKING_ID, 1, 5 * EXAMSG_MSG_MAX); EXA_ASSERT(err == 0); os_sem_post(&nbd_server.mailbox_sem); while (nbd_server.run) { device_t *device; ExamsgNbdLock nbd_lock_msg; ExamsgMID from; struct timeval timeout = { .tv_sec = 0, .tv_usec = 100000 }; exa_nodeset_t dest_nodes; err = examsgWaitTimeout(mh, &timeout); /* Just in order to check stopping the thread is required*/ if (err == -ETIME) continue; if (err != 0) { exalog_error("Locking thread encountered error %s (%d) while " "waiting in event loop.", exa_error_msg(err), err); continue; } err = examsgRecv(mh, &from, &nbd_lock_msg, sizeof(nbd_lock_msg)); /* No message */ if (err == 0) continue; if (err < 0) { exalog_error("Locking thread encountered error %s (%d) while " "receiving a messsage.", exa_error_msg(err), err); continue; } switch(nbd_lock_msg.any.type) { case EXAMSG_NBD_LOCK: /* find device from name */ /* FIXME devices lock is not held... it should */ device = find_device_from_uuid(&nbd_lock_msg.disk_uuid); if (device == NULL) { exalog_error("Unknown device with UUID " UUID_FMT, UUID_VAL(&nbd_lock_msg.disk_uuid)); err = -CMD_EXP_ERR_UNKNOWN_DEVICE; break; } if (nbd_lock_msg.lock) { err = exa_disk_lock_zone(device, nbd_lock_msg.locked_zone_start, nbd_lock_msg.locked_zone_size); EXA_ASSERT_VERBOSE(err == 0, "Trying to lock too many zone " "(>%d). Last zone not succesfully locked " "(start = %" PRId64 ", size = %" PRId64 " ) " "on device UUID " UUID_FMT, NBMAX_DISK_LOCKED_ZONES, nbd_lock_msg.locked_zone_start, nbd_lock_msg.locked_zone_size, UUID_VAL(&nbd_lock_msg.disk_uuid)); } else { err = exa_disk_unlock_zone(device, nbd_lock_msg.locked_zone_start, nbd_lock_msg.locked_zone_size); EXA_ASSERT_VERBOSE(err == 0, "Trying to unlock a never locked " "zone (unlocked zone start =%" PRId64 ", " "unlocked zone size = %" PRId64 ") on device" " UUID " UUID_FMT, nbd_lock_msg.locked_zone_start, nbd_lock_msg.locked_zone_size, UUID_VAL(&nbd_lock_msg.disk_uuid)); } break; default: /* error */ EXA_ASSERT_VERBOSE(false, "Locking thread got unknown message of" " type %d ", nbd_lock_msg.any.type); break; } exa_nodeset_single(&dest_nodes, from.netid.node); examsgAckReply(mh, (Examsg *)&nbd_lock_msg, err, from.id, &dest_nodes); } examsgDelMbox(mh, EXAMSG_NBD_LOCKING_ID); examsgExit(mh); } /** get the number of sector of the device * \param device_path the device to get the number of sector * \param nb_sectors64 the number of sectors of the device * \return nb_sectors the returned number of sector */ static int get_nb_sectors(const char *device_path, uint64_t *nb_sectors) { uint64_t device_size; /* in bytes */ int retval; int fd; /* We need the read access to get the size. */ if ((fd = os_disk_open_raw(device_path, OS_DISK_READ)) < 0) { exalog_error("cannot open device '%s' error=%s ", device_path, exa_error_msg(-fd)); return -CMD_EXP_ERR_OPEN_DEVICE; } retval = os_disk_get_size(fd, &device_size); if (retval < 0) { exalog_error("os_disk_get_size() error=%s", exa_error_msg(retval)); if (close(fd) != 0) exalog_error("can't EVEN close dev '%s'", device_path); return -EXA_ERR_IOCTL; } retval = close(fd); if (retval < 0) { retval = -errno; exalog_error("cannot close device '%s' error=%s ", device_path, exa_error_msg(retval)); return -CMD_EXP_ERR_CLOSE_DEVICE; } *nb_sectors = device_size / SECTOR_SIZE; /* remove the size of the reserved area for storing admind info */ *nb_sectors -= RDEV_RESERVED_AREA_IN_SECTORS; /* Align the size on 1K * this is the best we can do to have the same size of devices on 2.4 and 2.6 kernels due to * the fact that kernel 2.4 rounds the size of devices with 1 K */ *nb_sectors -= *nb_sectors % (1024 / SECTOR_SIZE); return EXA_SUCCESS; }
static void local_exa_vldelete (int thr_nb, void *msg) { struct adm_group *group; struct adm_volume *volume = NULL; int ret; /* used for local function calls */ int barrier_ret; /* used for barriers return values */ int undo_ret; struct vldelete_info *info = msg; group = adm_group_get_group_by_name(info->group_name); if (group == NULL) { ret = -ADMIND_ERR_UNKNOWN_GROUPNAME; goto get_barrier; } volume = adm_group_get_volume_by_name(group, info->volume_name); if (volume == NULL) { ret = -ADMIND_ERR_UNKNOWN_VOLUMENAME; goto get_barrier; } get_barrier: /*** Barrier: getting parameters ***/ ret = EXA_SUCCESS; barrier_ret = admwrk_barrier(thr_nb, ret, "Getting parameters"); if (barrier_ret != EXA_SUCCESS) goto local_exa_vldelete_end_no_resume; ret = vrt_group_suspend_threads_barrier(thr_nb, &group->uuid); if (ret != EXA_SUCCESS) goto local_exa_vldelete_end; /*** Action: mark the transaction as in-progress ***/ /* This is an in-memory operation, we assume it won't fail */ volume->committed = false; ret = conf_save_synchronous(); EXA_ASSERT_VERBOSE(ret == EXA_SUCCESS, "%s", exa_error_msg(ret)); /*** Barrier: mark the transaction as in-progress ***/ barrier_ret = admwrk_barrier(thr_nb, ret, "Marking transaction as in-progress"); if (barrier_ret == -ADMIND_ERR_NODE_DOWN) goto metadata_corruption; else if (barrier_ret != EXA_SUCCESS) goto local_exa_vldelete_end; /* XXX should errors be handled ? */ lum_exports_remove_export_from_uuid(&volume->uuid); lum_exports_increment_version(); lum_serialize_exports(); /*** Action: delete the volume (in memory) through the VRT API ***/ ret = vrt_client_volume_delete(adm_wt_get_localmb(), &group->uuid, &volume->uuid); /*** Barrier: delete the volume through the VRT API ***/ barrier_ret = admwrk_barrier(thr_nb, ret, "Deleting volume"); if (barrier_ret == -ADMIND_ERR_NODE_DOWN) goto metadata_corruption; else if (barrier_ret == -VRT_ERR_GROUP_NOT_ADMINISTRABLE) { /* Mark the transaction as committed, so that the volume is not * shown as "invalid" later. */ volume->committed = true; undo_ret = conf_save_synchronous(); EXA_ASSERT_VERBOSE(undo_ret == EXA_SUCCESS, "%s", exa_error_msg(undo_ret)); goto local_exa_vldelete_end; } else if ((barrier_ret != EXA_SUCCESS) && !info->metadata_recovery) goto local_exa_vldelete_end; /*** Action: group sync SB (master) ***/ ret = adm_vrt_group_sync_sb(thr_nb, group); /*** Barrier: group sync SB ***/ barrier_ret = admwrk_barrier(thr_nb, ret, "Syncing metadata on disk"); if (barrier_ret == -ADMIND_ERR_NODE_DOWN) goto metadata_corruption; else if (barrier_ret != EXA_SUCCESS) goto local_exa_vldelete_end; /* Delete the volume from the configuration */ adm_group_remove_volume(volume); adm_volume_free(volume); ret = conf_save_synchronous(); EXA_ASSERT_VERBOSE(ret == EXA_SUCCESS, "%s", exa_error_msg(ret)); barrier_ret = admwrk_barrier(thr_nb, ret, "Updating XML configuration"); if (barrier_ret == -ADMIND_ERR_NODE_DOWN) goto metadata_corruption; goto local_exa_vldelete_end; metadata_corruption: ret = -ADMIND_ERR_METADATA_CORRUPTION; local_exa_vldelete_end: barrier_ret = vrt_group_resume_threads_barrier(thr_nb, &group->uuid); /* What to do if that fails... I don't know. */ if (barrier_ret != 0) ret = barrier_ret; local_exa_vldelete_end_no_resume: exalog_debug("local_exa_vldelete() = %s", exa_error_msg(ret)); admwrk_ack(thr_nb, ret); }
/** * Initialize the RDEV service: * - start exa_rdev kernel module, * - allocate and initialize aligned buffers to read/writes superblocks. */ static int rdev_init(int thr_nb) { char path[OS_PATH_MAX]; int err = 0; if (os_kmod_load("exa_rdev") != 0) { exalog_error("Failed to load kernel module 'exa_rdev'"); return -ADMIND_ERR_MODULESTART; } /* Load the broken disks table */ err = exa_env_make_path(path, sizeof(path), exa_env_cachedir(), "broken_disks"); if (err != 0) return err; err = broken_disk_table_load(&broken_disks, path, true /* open_read_write */); if (err != 0) { exalog_error("Failed loading the broken disk table: %s (%d)", exa_error_msg(err), err); return err; } /* Initialize the rdev module */ err = exa_rdev_static_init(RDEV_STATIC_CREATE); if (err != 0) { exalog_error("Failed initializing rdev statics: %s (%d)", exa_error_msg(err), err); goto cleanup_broken; } exa_rdev_fd = exa_rdev_init(); if (exa_rdev_fd <= 0) { err = exa_rdev_fd; exalog_error("Failed initializing rdev: %s (%d)", exa_error_msg(err), err); goto cleanup_broken; } mh = examsgInit(EXAMSG_RDEV_ID); if (!mh) { exalog_error("Failed initializing messaging for disk checking thread"); err = -ENOMEM; goto cleanup_rdev_fd; } COMPILE_TIME_ASSERT(RDEV_SUPERBLOCK_SIZE <= SECTORS_TO_BYTES(RDEV_RESERVED_AREA_IN_SECTORS)); rdev_check_buffer = os_aligned_malloc(RDEV_SUPERBLOCK_SIZE, 4096, NULL); if (!rdev_check_buffer) { exalog_error("Failed allocating disk checking buffer"); err = -ENOMEM; goto cleanup_mh; } /* make sure the check thread will not quit */ quit = false; /* launch the rdev checking thread */ if (!exathread_create_named(&rdev_check_id, MIN_THREAD_STACK_SIZE, disk_checking_thread, mh, "rdev_check")) { exalog_error("Failed creating disk checking thread"); err = -EXA_ERR_DEFAULT; goto cleanup_rdev_check_buffer; } rdev_check_id_started = true; return EXA_SUCCESS; cleanup_rdev_check_buffer: os_aligned_free(rdev_check_buffer); rdev_check_buffer = NULL; cleanup_mh: examsgExit(mh); mh = NULL; cleanup_rdev_fd: close(exa_rdev_fd); exa_rdev_fd = -1; cleanup_broken: broken_disk_table_unload(&broken_disks); return err; }
static void disk_checking_thread(void *dummy) { exalog_as(EXAMSG_RDEV_ID); while (!quit) { int rdev_need_check = false; struct adm_disk *disk; adm_node_lock_disk_removal(); adm_node_for_each_disk(adm_myself(), disk) { if (disk->local->rdev_req != NULL) { int state, last_state; last_state = disk->local->state; state = exa_rdev_test(disk->local->rdev_req, rdev_check_buffer, RDEV_SUPERBLOCK_SIZE); /* if exa_rdev_test returns an error, the disk is considered in failure * as we have no mean to know what really happened. */ if (state < 0) { exalog_error("testing rdev '%s' " UUID_FMT " failed: %s (%d)", disk->path, UUID_VAL(&disk->uuid), exa_error_msg(state), state); state = EXA_RDEV_STATUS_FAIL; } if (state != last_state) { if (state == EXA_RDEV_STATUS_FAIL) rdev_need_check = true; disk->local->state = state; } } } adm_node_unlock_disk_removal(); if (quit) break; if (rdev_need_check) { instance_event_msg_t msg; int ret; msg.any.type = EXAMSG_EVMGR_INST_EVENT; msg.event.id = EXAMSG_RDEV_ID; msg.event.state = INSTANCE_CHECK_DOWN; msg.event.node_id = adm_myself()->id; exalog_info("... broadcasting action: rdev check down"); ret = examsgSend(mh, EXAMSG_ADMIND_EVMGR_ID, EXAMSG_ALLHOSTS, &msg, sizeof(msg)); EXA_ASSERT(ret == sizeof(msg)); } os_sleep(DISK_CHECK_INTERVAL); } }
int adm_vrt_group_sync_sb(int thr_nb, struct adm_group *group) { struct { bool group_is_started; bool can_write; bool have_disk_in_group; } info, reply; exa_nodeid_t nid; bool group_is_started_somewhere = false; int ret; int barrier_ret = EXA_SUCCESS; admwrk_request_t rpc; struct adm_disk *disk; int nb_nodes_with_writable_disks = 0; int nb_nodes_with_disks_in_group = 0; uint64_t old_sb_version, new_sb_version; COMPILE_TIME_ASSERT(sizeof(info) <= ADM_MAILBOX_PAYLOAD_PER_NODE); /* XXX maybe checking started is useless as administrable => started * and !administrable => return */ info.group_is_started = group->started; info.can_write = false; info.have_disk_in_group = false; adm_group_for_each_disk(group, disk) { if (disk->node_id == adm_my_id) { info.have_disk_in_group = true; if (disk->up_in_vrt) info.can_write = true; } } admwrk_bcast(thr_nb, &rpc, EXAMSG_SERVICE_VRT_SB_SYNC, &info, sizeof(info)); while (admwrk_get_bcast(&rpc, &nid, &reply, sizeof(reply), &ret)) { if (ret == -ADMIND_ERR_NODE_DOWN) { barrier_ret = -ADMIND_ERR_NODE_DOWN; continue; } EXA_ASSERT(ret == EXA_SUCCESS); if (reply.can_write) nb_nodes_with_writable_disks++; if (reply.have_disk_in_group) nb_nodes_with_disks_in_group++; if (reply.group_is_started) group_is_started_somewhere = true; } if (barrier_ret != EXA_SUCCESS) return barrier_ret; /* do not write superblocks if the group is stopped on all nodes */ if (!group_is_started_somewhere) return EXA_SUCCESS; if (nb_nodes_with_writable_disks < quotient_ceil64(nb_nodes_with_disks_in_group, 2)) return -VRT_ERR_GROUP_NOT_ADMINISTRABLE; old_sb_version = sb_version_get_version(group->sb_version); new_sb_version = sb_version_new_version_prepare(group->sb_version); if (group->started) { ret = vrt_client_group_sync_sb(adm_wt_get_localmb(), &group->uuid, old_sb_version, new_sb_version); EXA_ASSERT_VERBOSE(ret == EXA_SUCCESS || ret == -ADMIND_ERR_NODE_DOWN, "Synchronization of superblocks failed for group '%s' " "UUID=" UUID_FMT ": %s (%d)", group->name, UUID_VAL(&group->uuid), exa_error_msg(ret), ret); } else ret = EXA_SUCCESS; barrier_ret = admwrk_barrier(thr_nb, ret, "VRT: Preparing superblocks version"); if (barrier_ret != EXA_SUCCESS) return barrier_ret; sb_version_new_version_done(group->sb_version); barrier_ret = admwrk_barrier(thr_nb, EXA_SUCCESS, "VRT: Writing superblocks version"); /* Commit anyway, If we are here, we are sure that other nodes have done the * job too even if they crashed meanwhile */ sb_version_new_version_commit(group->sb_version); return barrier_ret; }
/** * \brief receive a data from a socket. * When returning EXA_SUCCESS, xml_tree points on a valid xml tree. * * \param[in] sock_fd socket file descriptor of the incoming data * \param[out] xml_tree Pointer on the xmlDocPtr pointing on a XML tree * extracted from the data read on the socket. * * \return EXA_SUCCESS or a negative error. */ static int receive(int sock_fd, char **buffer) { int current_read = 0; char *buffer_receiving = NULL; EXA_ASSERT(buffer); *buffer = NULL; exalog_debug("SOCKET %d: XML Message processing", sock_fd); /* We read and merge each chunk of incomming data in the hope to get * the END of command. Here the input buffer is sized as to handle * all the command in one chunk, so not having the end of command is * an error. */ do { int nb_car; char *new_buffer_receiving; if (current_read > PROTOCOL_BUFFER_MAX_SIZE) { cl_error_desc_t error; set_error(&error, -EXA_ERR_CMD_PARSING, "Received command is bigger than our internal limit (%d bytes)", PROTOCOL_BUFFER_MAX_SIZE); exalog_error("Socket %d peer '%s': %s", sock_fd, cli_peer_from_fd(sock_fd), error.msg); cli_command_end_complete(sock_fd, &error); /* Don't need the receiving buffer any more */ os_free(buffer_receiving); return -EXA_ERR_CMD_PARSING; } new_buffer_receiving = os_realloc(buffer_receiving, PROTOCOL_BUFFER_LEN + current_read + sizeof(char) /* for '\0' */); if (!new_buffer_receiving) { os_free(buffer_receiving); exalog_error("Socket %d peer '%s': Failed to realloc()", sock_fd, cli_peer_from_fd(sock_fd)); return -ENOMEM; } buffer_receiving = new_buffer_receiving; do nb_car = os_recv(sock_fd, buffer_receiving + current_read, PROTOCOL_BUFFER_LEN, 0); while (nb_car == -EINTR); if (nb_car == 0) { exalog_debug("SOCKET %d: Error = Client did disconnect itself", sock_fd); os_free(buffer_receiving); return -ECONNABORTED; } if (nb_car < 0 && errno != EAGAIN) { if (nb_car == -ECONNRESET) exalog_debug("SOCKET %d: Error %d", sock_fd, nb_car); else exalog_error("Socket %d peer '%s': %s (%d)", sock_fd, cli_peer_from_fd(sock_fd), exa_error_msg(nb_car), nb_car); os_free(buffer_receiving); return nb_car; } if(nb_car > 0) current_read += nb_car; /* Make sure to be properly ended */ buffer_receiving[current_read] = '\0'; /* Make sure the XML input is complete */ } while(xml_buffer_is_partial(buffer_receiving)); exalog_debug("SOCKET %d: Receive Message lg = %" PRIzu " : [%s]", sock_fd, strlen(buffer_receiving), buffer_receiving); *buffer = buffer_receiving; return EXA_SUCCESS; }
/** * Process a connection that has incoming data. * * \param[in] conn_id Connection id * \param[in] sock_fd Connection socket */ static void handle_inputdata(int conn_id, int sock_fd) { char *buffer = NULL; void *data = NULL; size_t data_size; adm_command_code_t cmd_code; const struct AdmCommand *command; exa_uuid_t cluster_uuid; cl_error_desc_t err_desc; int retval; /* Receive the xml tree parsed in message */ retval = receive(sock_fd, &buffer); if (retval < 0) { if (retval == -ECONNRESET || retval == -ECONNABORTED) exalog_debug("CONNECTION %d: An error occurred : %d [%s]", conn_id, retval, exa_error_msg(retval)); else exalog_error("Socket %d peer '%s': An error occurred : %s (%d)", sock_fd, cli_peer_from_fd(sock_fd), exa_error_msg(retval), retval); close_connection(conn_id); return; } /* Parse the tree we just received and get a newly allocated payload data */ xml_command_parse(buffer, &cmd_code, &cluster_uuid, &data, &data_size, &err_desc); /* buffer is now parsed, let's free it */ os_free(buffer); if (err_desc.code != EXA_SUCCESS) { /* No need to free data buffer if parsing returned an error */ exalog_error("Failed to parse command on socket %d (from peer '%s'): %s (%d)", sock_fd, cli_peer_from_fd(sock_fd), err_desc.msg, err_desc.code); cli_command_end_complete(sock_fd, &err_desc); return; } command = adm_command_find(cmd_code); EXA_ASSERT_VERBOSE(command, "Missing implementation of command #%d", cmd_code); connectlist[conn_id].uid = get_new_cmd_uid(); retval = send_command_to_evmgr(connectlist[conn_id].uid, command, &cluster_uuid, data, data_size); if (retval < 0) { if (retval == -EXA_ERR_ADM_BUSY) exalog_warning("Running command %s (request from %s) failed: %s", adm_command_find(cmd_code)->msg, cli_get_peername(connectlist[conn_id].uid), exa_error_msg(retval)); else exalog_error("Running command %s (request from %s) failed: %s (%d)", adm_command_find(cmd_code)->msg, cli_get_peername(connectlist[conn_id].uid), exa_error_msg(retval), retval); set_error(&err_desc, retval, NULL); cli_command_end_complete(sock_fd, &err_desc); /* the command was not scheduled, reset the uid */ connectlist[conn_id].uid = CMD_UID_INVALID; } os_free(data); }
int main(int argc, char **argv) { int di; char *cp; int inFd = -1; int outFd = -1; int inCc = 0; int outCc; char *inFile; char *outFile; uint64_t blockSize, readSize; uint64_t size; uint64_t intotal; uint64_t outTotal; char *buf = NULL; char hex_output[16 * 2 + 1]; self = strrchr(argv[0], '/'); if (self == NULL) self = argv[0]; else self++; /* Parse any options */ if (argc != 5) { fprintf(stderr, "dd with md5 checksum.\n"); fprintf(stderr, "\n"); fprintf(stderr, "usage: %s <if> <of> <bufsize> <count>\n", self); fprintf(stderr, "\n"); fprintf(stderr, " <if> Input file\n"); fprintf(stderr, " <of> Output file\n"); fprintf(stderr, " <bufsize> Size of buffer\n"); fprintf(stderr, " <count> Number of bytes to copy\n"); fprintf(stderr, "\n"); fprintf(stderr, "NOTES:\n"); fprintf(stderr, " - The actual number of bytes copied may be greater" " than the specified count\n" " because of the buffer size.\n"); fprintf(stderr, " - The computed md5 is appended to the end of the" " output file (no idea why).\n"); exit(1); } inFile = argv[1]; outFile = argv[2]; if (to_uint64(argv[3], &blockSize) != EXA_SUCCESS) { error("invalid buffer size: '%s'", argv[3]); goto done; } if (to_uint64(argv[4], &size) != EXA_SUCCESS) { error("invalid byte count: '%s'", argv[4]); goto done; } if ((buf = malloc(blockSize)) == NULL) { error("alloc buffer: %s", exa_error_msg(-errno)); goto done; } intotal = 0; outTotal = 0; /* Open the source file*/ inFd = open(inFile, 0); if (inFd < 0) { error("open input file %s: %s",inFile, exa_error_msg(-errno)); goto done; } /* Open the dest file*/ outFd = open(outFile, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (outFd < 0) { error("open output file %s: %s",outFile, exa_error_msg(-errno)); goto done; } lseek(inFd, 0, SEEK_SET); lseek(outFd, 0, SEEK_SET); readSize = blockSize; /* Write the file with block */ while (outTotal < size) { /* Read the source file */ inCc = read(inFd, buf, readSize); if (inCc <= 0) { error("read: %s", exa_error_msg(-errno)); goto done; } intotal += inCc; cp = buf; outCc = 0; /* Write on the dest file */ while (outCc < inCc) { md5_state_t state; md5_byte_t digest[16]; outCc = write(outFd, cp, inCc); if (outCc <= 0) { error("write: %s", exa_error_msg(-errno)); goto done; } inCc -= outCc; outTotal += outCc; /* Manage the check sum */ md5_init(&state); md5_append(&state, (const md5_byte_t *)cp,outCc); md5_finish(&state, digest); for (di = 0; di < 16; ++di) sprintf(hex_output + di * 2, "%02x", digest[di]); cp += outCc; } /* End of the file */ if (size - outTotal < blockSize) readSize = size - outTotal; } lseek(outFd, 0,SEEK_END); if (write(outFd, hex_output, strlen(hex_output)) <= 0) { error("write md5: %s", exa_error_msg(-errno)); goto done; } done: if (inFd != -1 && close(inFd) != 0) error("close %s: %s", inFile, exa_error_msg(-errno)); if (outFd != -1 && close(outFd) != 0) error("close %s: %s", outFile, exa_error_msg(-errno)); sync(); free(buf); return err ? 1 : 0; }