/*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/** \file client_common.c Common functions of para_client and para_audiod. */
+#include <netinet/in.h>
+#include <sys/socket.h>
#include <regex.h>
#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
#include "para.h"
#include "error.h"
#include "list.h"
#include "sched.h"
-#include "client.cmdline.h"
#include "crypt.h"
#include "net.h"
#include "fd.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 ressources.
- *
- * \param ct The client task.
- *
- * This frees all ressources 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);
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;
}
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 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 void 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)
- return;
+ return 0;
switch (ct->status) {
case CL_CONNECTED: /* receive welcome message */
ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &s->rfds, &n);
goto out;
}
ct->status = CL_RECEIVED_WELCOME;
- return;
+ return 0;
case CL_RECEIVED_WELCOME: /* send auth command */
if (!FD_ISSET(ct->scc.fd, &s->wfds))
- return;
- sprintf(buf, AUTH_REQUEST_MSG "%s sideband", ct->user);
+ return 0;
+ sprintf(buf, AUTH_REQUEST_MSG "%s sideband%s", ct->user,
+ has_feature("aes_ctr128", ct)? ",aes_ctr128" : "");
PARA_INFO_LOG("--> %s\n", buf);
ret = write_buffer(ct->scc.fd, buf);
if (ret < 0)
goto out;
ct->status = CL_SENT_AUTH;
- return;
+ return 0;
case CL_SENT_AUTH:
/*
* Receive challenge and session keys, decrypt the challenge and
/* 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)
goto out;
ct->challenge_hash = para_malloc(HASH_SIZE);
hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
- ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
+ 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);
+ SESSION_KEY_LEN, use_aes);
hash_to_asc(ct->challenge_hash, buf);
PARA_INFO_LOG("--> %s\n", buf);
ct->status = CL_RECEIVED_CHALLENGE;
- return;
+ return 0;
}
case CL_RECEIVED_CHALLENGE:
ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE,
case CL_RECEIVED_PROCEED: /* concat args and send command */
{
if (!FD_ISSET(ct->scc.fd, &s->wfds))
- return;
+ return 0;
ret = send_sb_command(ct);
if (ret <= 0)
goto out;
ct->status = CL_EXECUTING;
- return;
+ return 0;
}
case CL_SENDING:
if (ct->btrn[1]) {
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);
PARA_INFO_LOG("channel 1: %s\n", para_strerror(-ret));
btr_remove_node(&ct->btrn[1]);
if (ct->btrn[0])
- return;
+ return 0;
goto out;
close0:
PARA_INFO_LOG("channel 0: %s\n", para_strerror(-ret));
btr_remove_node(&ct->btrn[0]);
if (ct->btrn[1] && ct->status == CL_SENDING)
- return;
+ return 0;
out:
if (ret >= 0)
- return;
+ return 0;
btr_remove_node(&ct->btrn[0]);
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));
- t->error = 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;
}
/**
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)
+{
+ struct ggo_help h = DEFINE_GGO_HELP(client);
+ bool d = ct->conf.detailed_help_given;
+
+ ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
+ exit(0);
+}
+
/**
* Parse a client configuration.
*
ret = -E_CLIENT_SYNTAX;
if (client_cmdline_parser(argc, argv, &ct->conf))
goto out;
- HANDLE_VERSION_FLAG("client", ct->conf);
+ 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) :