+int fio_handle_client(struct fio_client *client)
+{
+ struct client_ops *ops = client->ops;
+ struct fio_net_cmd *cmd;
+
+ dprint(FD_NET, "client: handle %s\n", client->hostname);
+
+ cmd = fio_net_recv_cmd(client->fd);
+ if (!cmd)
+ return 0;
+
+ dprint(FD_NET, "client: got cmd op %s from %s (pdu=%u)\n",
+ fio_server_op(cmd->opcode), client->hostname, cmd->pdu_len);
+
+ switch (cmd->opcode) {
+ case FIO_NET_CMD_QUIT:
+ if (ops->quit)
+ ops->quit(client, cmd);
+ remove_client(client);
+ break;
+ case FIO_NET_CMD_TEXT:
+ convert_text(cmd);
+ ops->text(client, cmd);
+ break;
+ case FIO_NET_CMD_DU: {
+ struct cmd_du_pdu *du = (struct cmd_du_pdu *) cmd->payload;
+
+ convert_dus(&du->dus);
+ convert_agg(&du->agg);
+
+ ops->disk_util(client, cmd);
+ break;
+ }
+ case FIO_NET_CMD_TS: {
+ struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
+
+ convert_ts(&p->ts, &p->ts);
+ convert_gs(&p->rs, &p->rs);
+
+ ops->thread_status(client, cmd);
+ break;
+ }
+ case FIO_NET_CMD_GS: {
+ struct group_run_stats *gs = (struct group_run_stats *) cmd->payload;
+
+ convert_gs(gs, gs);
+
+ ops->group_stats(client, cmd);
+ break;
+ }
+ case FIO_NET_CMD_ETA: {
+ struct jobs_eta *je = (struct jobs_eta *) cmd->payload;
+
+ remove_reply_cmd(client, cmd);
+ convert_jobs_eta(je);
+ handle_eta(client, cmd);
+ break;
+ }
+ case FIO_NET_CMD_PROBE:
+ remove_reply_cmd(client, cmd);
+ ops->probe(client, cmd);
+ break;
+ case FIO_NET_CMD_SERVER_START:
+ client->state = Client_running;
+ if (ops->job_start)
+ ops->job_start(client, cmd);
+ break;
+ case FIO_NET_CMD_START: {
+ struct cmd_start_pdu *pdu = (struct cmd_start_pdu *) cmd->payload;
+
+ pdu->jobs = le32_to_cpu(pdu->jobs);
+ ops->start(client, cmd);
+ break;
+ }
+ case FIO_NET_CMD_STOP: {
+ struct cmd_end_pdu *pdu = (struct cmd_end_pdu *) cmd->payload;
+
+ convert_stop(cmd);
+ client->state = Client_stopped;
+ client->error = le32_to_cpu(pdu->error);
+ client->signal = le32_to_cpu(pdu->signal);
+ ops->stop(client, cmd);
+ break;
+ }
+ case FIO_NET_CMD_ADD_JOB: {
+ struct cmd_add_job_pdu *pdu = (struct cmd_add_job_pdu *) cmd->payload;
+
+ client->thread_number = le32_to_cpu(pdu->thread_number);
+ client->groupid = le32_to_cpu(pdu->groupid);
+
+ if (ops->add_job)
+ ops->add_job(client, cmd);
+ break;
+ }
+ case FIO_NET_CMD_IOLOG:
+ if (ops->iolog) {
+ struct cmd_iolog_pdu *pdu;
+
+ pdu = convert_iolog(cmd);
+ ops->iolog(client, pdu);
+ }
+ break;
+ case FIO_NET_CMD_UPDATE_JOB:
+ ops->update_job(client, cmd);
+ remove_reply_cmd(client, cmd);
+ break;
+ case FIO_NET_CMD_VTRIGGER: {
+ struct all_io_list *pdu = (struct all_io_list *) cmd->payload;
+ char buf[64];
+
+ __verify_save_state(pdu, server_name(client, buf, sizeof(buf)));
+ exec_trigger(trigger_cmd);
+ break;
+ }
+ case FIO_NET_CMD_SENDFILE: {
+ struct cmd_sendfile *pdu = (struct cmd_sendfile *) cmd->payload;
+ send_file(client, pdu, cmd->tag);
+ break;
+ }
+ default:
+ log_err("fio: unknown client op: %s\n", fio_server_op(cmd->opcode));
+ break;
+ }
+
+ free(cmd);
+ return 1;
+}
+
+int fio_clients_send_trigger(const char *cmd)
+{
+ struct flist_head *entry;
+ struct fio_client *client;
+ size_t slen;
+
+ dprint(FD_NET, "client: send vtrigger: %s\n", cmd);
+
+ if (!cmd)
+ slen = 0;
+ else
+ slen = strlen(cmd);
+
+ flist_for_each(entry, &client_list) {
+ struct cmd_vtrigger_pdu *pdu;
+
+ client = flist_entry(entry, struct fio_client, list);
+
+ pdu = malloc(sizeof(*pdu) + slen);
+ pdu->len = cpu_to_le16((uint16_t) slen);
+ if (slen)
+ memcpy(pdu->cmd, cmd, slen);
+ fio_net_send_cmd(client->fd, FIO_NET_CMD_VTRIGGER, pdu,
+ sizeof(*pdu) + slen, NULL, NULL);
+ free(pdu);
+ }
+
+ return 0;
+}
+
+static void request_client_etas(struct client_ops *ops)