-/*
- * Copyright (C) 1997-2014 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
+/* Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
/** \file client_common.c Common functions of para_client and para_audiod. */
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
+#include "client.lsg.h"
#include "para.h"
#include "error.h"
#include "list.h"
+#include "lsu.h"
#include "sched.h"
#include "crypt.h"
#include "net.h"
#include "fd.h"
#include "sideband.h"
#include "string.h"
-#include "client.cmdline.h"
#include "client.h"
#include "buffer_tree.h"
#include "version.h"
-#include "ggo.h"
/** The size of the receiving buffer. */
#define CLIENT_BUFSIZE 4000
-/**
- * Close the connection to para_server and deallocate per-command resources.
- *
- * \param ct The client task.
- *
- * This frees all resources of the current command but keeps the configuration
- * in \p ct->conf.
- *
- * \sa \ref client_close().
- */
-void client_disconnect(struct client_task *ct)
-{
- if (!ct)
- return;
- if (ct->scc.fd >= 0)
- close(ct->scc.fd);
- free_argv(ct->features);
- ct->features = NULL;
- sc_free(ct->scc.recv);
- ct->scc.recv = NULL;
- sc_free(ct->scc.send);
- ct->scc.send = NULL;
- btr_remove_node(&ct->btrn[0]);
- btr_remove_node(&ct->btrn[1]);
-}
-
/**
* Close the connection to para_server and free all resources.
*
* \param ct Pointer to the client data.
*
- * \sa \ref client_open(), \ref client_disconnect().
+ * \sa \ref client_open().
*/
void client_close(struct client_task *ct)
{
if (!ct)
return;
- client_disconnect(ct);
free(ct->user);
- free(ct->config_file);
free(ct->key_file);
- client_cmdline_parser_free(&ct->conf);
+ lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR);
free(ct->challenge_hash);
sb_free(ct->sbc[0]);
sb_free(ct->sbc[1]);
free(ct);
}
-/**
+/*
* The preselect hook for server commands.
*
- * \param s Pointer to the scheduler.
- * \param t Pointer to the task struct for this command.
- *
* The task pointer must contain a pointer to the initialized client data
* structure as it is returned by client_open().
*
* This function checks the state of the connection and adds the file descriptor
- * of the connection to the read or write fd set of \a s accordingly.
- *
- * \sa register_task() client_open(), struct sched, struct task.
+ * of the connection to the read or write fd set of s accordingly.
*/
-static void client_pre_select(struct sched *s, struct task *t)
+static void client_pre_select(struct sched *s, void *context)
{
int ret;
- struct client_task *ct = container_of(t, struct client_task, task);
+ struct client_task *ct = context;
if (ct->scc.fd < 0)
return;
else if (ret > 0)
para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
}
- /* fall though */
+ /* fallthrough */
case CL_EXECUTING:
if (ct->btrn[0]) {
ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT);
}
if (n == 0)
return 0;
- if (!sb_received(ct->sbc[0], n, result))
+ ret = sb_received(ct->sbc[0], n, result);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
goto again;
ct->sbc[0] = NULL;
return 1;
return ret;
}
-static bool has_feature(const char *feature, struct client_task *ct)
-{
- return find_arg(feature, ct->features) >= 0? true : false;
-}
-
static int send_sb_command(struct client_task *ct)
{
int i;
char *command, *p;
size_t len = 0;
+ unsigned num_inputs = lls_num_inputs(ct->lpr);
if (ct->sbc[1])
return send_sb(ct, 0, NULL, 0, 0, false);
- for (i = 0; i < ct->conf.inputs_num; i++)
- len += strlen(ct->conf.inputs[i]) + 1;
+ for (i = 0; i < num_inputs; i++)
+ len += strlen(lls_input(i, ct->lpr)) + 1;
p = command = para_malloc(len);
- for (i = 0; i < ct->conf.inputs_num; i++) {
- strcpy(p, ct->conf.inputs[i]);
- p += strlen(ct->conf.inputs[i]) + 1;
+ for (i = 0; i < num_inputs; i++) {
+ const char *str = lls_input(i, ct->lpr);
+ strcpy(p, str);
+ p += strlen(str) + 1;
}
PARA_DEBUG_LOG("--> %s\n", command);
return send_sb(ct, 0, command, len, SBD_COMMAND, false);
}
-/**
+/*
* The post select hook for client commands.
*
- * \param s Pointer to the scheduler.
- * \param t Pointer to the task struct for this command.
- *
* Depending on the current state of the connection and the status of the read
- * and write fd sets of \a s, this function performs the necessary steps to
- * authenticate the connection, to send the command given by \a t->private_data
+ * and write fd sets of s, this function performs the necessary steps to
+ * authenticate the connection, to send the command given by t->private_data
* and to receive para_server's output, if any.
- *
- * \sa struct sched, struct task.
*/
-static int client_post_select(struct sched *s, struct task *t)
+static int client_post_select(struct sched *s, void *context)
{
- struct client_task *ct = container_of(t, struct client_task, task);
+ struct client_task *ct = context;
int ret = 0;
size_t n;
char buf[CLIENT_BUFSIZE];
- ret = task_get_notification(t);
+ ret = task_get_notification(ct->task);
if (ret < 0)
goto out;
if (ct->scc.fd < 0)
if (ret < 0 || n == 0)
goto out;
ct->features = parse_features(buf);
- if (!has_feature("sideband", ct)) {
- PARA_ERROR_LOG("server has no sideband support\n");
- ret = -E_INCOMPAT_FEAT;
- goto out;
- }
ct->status = CL_RECEIVED_WELCOME;
return 0;
case CL_RECEIVED_WELCOME: /* send auth command */
if (!FD_ISSET(ct->scc.fd, &s->wfds))
return 0;
- sprintf(buf, AUTH_REQUEST_MSG "%s sideband%s", ct->user,
- has_feature("aes_ctr128", ct)? ",aes_ctr128" : "");
+ sprintf(buf, AUTH_REQUEST_MSG "%s sideband,aes_ctr128",
+ ct->user);
PARA_INFO_LOG("--> %s\n", buf);
ret = write_buffer(ct->scc.fd, buf);
if (ret < 0)
/* decrypted challenge/session key buffer */
unsigned char crypt_buf[1024];
struct sb_buffer sbb;
- bool use_aes;
ret = recv_sb(ct, &s->rfds, &sbb);
if (ret <= 0)
}
n = sbb.iov.iov_len;
PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
- ret = priv_decrypt(ct->key_file, crypt_buf,
+ ret = apc_priv_decrypt(ct->key_file, crypt_buf,
sbb.iov.iov_base, n);
free(sbb.iov.iov_base);
if (ret < 0)
goto out;
ct->challenge_hash = para_malloc(HASH_SIZE);
- hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
- use_aes = has_feature("aes_ctr128", ct);
- ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN, use_aes);
- ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
- SESSION_KEY_LEN, use_aes);
+ hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+ ct->scc.send = sc_new(crypt_buf + APC_CHALLENGE_SIZE, SESSION_KEY_LEN);
+ ct->scc.recv = sc_new(crypt_buf + APC_CHALLENGE_SIZE + SESSION_KEY_LEN,
+ SESSION_KEY_LEN);
hash_to_asc(ct->challenge_hash, buf);
PARA_INFO_LOG("--> %s\n", buf);
ct->status = CL_RECEIVED_CHALLENGE;
btr_consume(ct->btrn[1], sz);
}
}
- /* fall though */
+ /* fall through */
case CL_EXECUTING:
if (ct->btrn[0]) {
ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT);
btr_remove_node(&ct->btrn[1]);
if (ret != -E_SERVER_CMD_SUCCESS && ret != -E_SERVER_CMD_FAILURE)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ if (ct->scc.fd >= 0) {
+ close(ct->scc.fd);
+ ct->scc.fd = -1;
+ }
+ free_argv(ct->features);
+ ct->features = NULL;
+ sc_free(ct->scc.recv);
+ ct->scc.recv = NULL;
+ sc_free(ct->scc.send);
+ ct->scc.send = NULL;
return ret;
}
struct btr_node *parent, struct btr_node *child)
{
int ret;
+ const char *host = CLIENT_OPT_STRING_VAL(HOSTNAME, ct->lpr);
+ uint32_t port = CLIENT_OPT_UINT32_VAL(SERVER_PORT, ct->lpr);
- PARA_NOTICE_LOG("connecting %s:%d\n", ct->conf.hostname_arg,
- ct->conf.server_port_arg);
+ PARA_NOTICE_LOG("connecting %s:%u\n", host, port);
ct->scc.fd = -1;
- ret = para_connect_simple(IPPROTO_TCP, ct->conf.hostname_arg,
- ct->conf.server_port_arg);
+ ret = para_connect_simple(IPPROTO_TCP, host, port);
if (ret < 0)
return ret;
ct->scc.fd = ret;
EMBRACE(.name = "client recv", .parent = NULL, .child = child));
ct->btrn[1] = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = "client send", .parent = parent, .child = NULL));
- ct->task.pre_select = client_pre_select;
- ct->task.post_select = client_post_select;
- ct->task.error = 0;
- sprintf(ct->task.status, "client");
- register_task(s, &ct->task);
+
+ ct->task = task_register(&(struct task_info) {
+ .name = "client",
+ .pre_select = client_pre_select,
+ .post_select = client_post_select,
+ .context = ct,
+ }, s);
return 1;
err_out:
close(ct->scc.fd);
return ret;
}
-__noreturn static void print_help_and_die(struct client_task *ct)
+static void handle_help_flag(struct lls_parse_result *lpr)
{
- struct ggo_help h = DEFINE_GGO_HELP(client);
- bool d = ct->conf.detailed_help_given;
+ char *help;
- ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
- exit(0);
+ if (CLIENT_OPT_GIVEN(DETAILED_HELP, lpr))
+ help = lls_long_help(CLIENT_CMD_PTR);
+ else if (CLIENT_OPT_GIVEN(HELP, lpr))
+ help = lls_short_help(CLIENT_CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ exit(EXIT_SUCCESS);
}
/**
int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
int *loglevel)
{
- char *home = para_homedir();
- int ret;
- struct client_task *ct = para_calloc(sizeof(struct client_task));
+ const struct lls_command *cmd = CLIENT_CMD_PTR;
+ struct lls_parse_result *lpr;
+ int ret, ll;
+ struct client_task *ct;
+ char *kf = NULL, *user, *errctx, *home = para_homedir();
- *ct_ptr = ct;
- ct->scc.fd = -1;
- ret = -E_CLIENT_SYNTAX;
- if (client_cmdline_parser(argc, argv, &ct->conf))
+ ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+ if (ret < 0)
goto out;
- version_handle_flag("client", ct->conf.version_given);
- if (ct->conf.help_given || ct->conf.detailed_help_given)
- print_help_and_die(ct);
-
- ct->config_file = ct->conf.config_file_given?
- para_strdup(ct->conf.config_file_arg) :
- make_message("%s/.paraslash/client.conf", home);
- ret = file_exists(ct->config_file);
- if (!ret && ct->conf.config_file_given) {
- ret = -E_NO_CONFIG;
+ version_handle_flag("client", CLIENT_OPT_GIVEN(VERSION, lpr));
+ handle_help_flag(lpr);
+
+ ret = lsu_merge_config_file_options(CLIENT_OPT_STRING_VAL(CONFIG_FILE, lpr),
+ "client.conf", &lpr, cmd, client_suite, 0U /* default flags */);
+ if (ret < 0)
goto out;
- }
- if (ret) {
- struct client_cmdline_parser_params params = {
- .override = 0,
- .initialize = 0,
- .check_required = 0,
- .check_ambiguity = 0,
- .print_errors = 0
- };
- ret = -E_BAD_CONFIG;
- if (client_cmdline_parser_config_file(ct->config_file,
- &ct->conf, ¶ms))
- goto out;
- }
- ct->user = ct->conf.user_given?
- para_strdup(ct->conf.user_arg) : para_logname();
+ /* success */
+ ll = CLIENT_OPT_UINT32_VAL(LOGLEVEL, lpr);
+ if (loglevel)
+ *loglevel = ll;
+ user = CLIENT_OPT_GIVEN(USER, lpr)?
+ para_strdup(CLIENT_OPT_STRING_VAL(USER, lpr)) : para_logname();
- if (ct->conf.key_file_given)
- ct->key_file = para_strdup(ct->conf.key_file_arg);
+ if (CLIENT_OPT_GIVEN(KEY_FILE, lpr))
+ kf = para_strdup(CLIENT_OPT_STRING_VAL(KEY_FILE, lpr));
else {
- ct->key_file = make_message("%s/.paraslash/key.%s",
- home, ct->user);
- if (!file_exists(ct->key_file)) {
- free(ct->key_file);
- ct->key_file = make_message("%s/.ssh/id_rsa", home);
+ kf = make_message("%s/.paraslash/key.%s", home, user);
+ if (!file_exists(kf)) {
+ free(kf);
+ kf = make_message("%s/.ssh/id_rsa", home);
}
}
-
- if (loglevel)
- *loglevel = get_loglevel_by_name(ct->conf.loglevel_arg);
- PARA_INFO_LOG("loglevel: %s\n", ct->conf.loglevel_arg);
- PARA_INFO_LOG("config_file: %s\n", ct->config_file);
- PARA_INFO_LOG("key_file: %s\n", ct->key_file);
- ret = ct->conf.inputs_num;
+ PARA_INFO_LOG("user: %s\n", user);
+ PARA_INFO_LOG("key file: %s\n", kf);
+ PARA_INFO_LOG("loglevel: %d\n", ll);
+ ct = para_calloc(sizeof(*ct));
+ ct->scc.fd = -1;
+ ct->lpr = lpr;
+ ct->key_file = kf;
+ ct->user = user;
+ *ct_ptr = ct;
+ ret = lls_num_inputs(lpr);
out:
free(home);
if (ret < 0) {
- PARA_ERROR_LOG("%s\n", para_strerror(-ret));
- client_close(ct);
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ lls_free_parse_result(lpr, cmd);
+ free(kf);
*ct_ptr = NULL;
}
return ret;