Merge branch 'refs/heads/t/ff'
[paraslash.git] / client_common.c
index 3b8ed51..a7eee85 100644 (file)
@@ -1,8 +1,4 @@
-/*
- * Copyright (C) 1997-2013 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;
@@ -122,7 +86,7 @@ static void client_pre_select(struct sched *s, struct task *t)
                        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);
@@ -191,7 +155,10 @@ again:
        }
        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;
@@ -270,52 +237,44 @@ out:
        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)
@@ -326,18 +285,13 @@ static int client_post_select(struct sched *s, struct task *t)
                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)
@@ -353,7 +307,6 @@ static int client_post_select(struct sched *s, struct task *t)
                /* 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)
@@ -365,17 +318,16 @@ static int client_post_select(struct sched *s, struct task *t)
                }
                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;
@@ -437,7 +389,7 @@ static int client_post_select(struct sched *s, struct task *t)
                                        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);
@@ -476,6 +428,16 @@ out:
        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;
 }
 
@@ -496,12 +458,12 @@ int client_connect(struct client_task *ct, struct sched *s,
                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;
@@ -513,11 +475,13 @@ int client_connect(struct client_task *ct, struct sched *s,
                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);
@@ -525,13 +489,19 @@ err_out:
        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);
 }
 
 /**
@@ -555,65 +525,56 @@ __noreturn static void print_help_and_die(struct client_task *ct)
 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, &params))
-                       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;