uname_rs := $(shell uname -rs)
cc_version := $(shell $(CC) --version | head -n 1)
GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
-COPYRIGHT_YEAR := 2018
+COPYRIGHT_YEAR := 2019
ifeq ("$(origin O)", "command line")
build_dir := $(O)
CPPFLAGS += -DLOGLEVELS='$(LOGLEVELS)'
CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
-CPPFLAGS += -I/usr/local/include
CPPFLAGS += -I$(lls_suite_dir)
CPPFLAGS += -I$(yy_build_dir)
CPPFLAGS += $(lopsub_cppflags)
$(object_dir)/flac%.o: CPPFLAGS += $(flac_cppflags)
$(object_dir)/mp3_afh.o: CPPFLAGS += $(id3tag_cppflags)
-$(object_dir)/crypt.o: CPPFLAGS += $(openssl_cppflags)
+$(object_dir)/openssl.o: CPPFLAGS += $(openssl_cppflags)
$(object_dir)/gcrypt.o: CPPFLAGS += $(gcrypt_cppflags)
$(object_dir)/ao_write.o: CPPFLAGS += $(ao_cppflags)
$(object_dir)/alsa%.o: CPPFLAGS += $(alsa_cppflags)
para_printf(&aca->pbout, "activating dummy mood\n");
activate_mood_or_playlist(NULL, &num_admissible, NULL);
out:
- para_printf(&aca->pbout, "activated %s (%d admissible files)\n",
- current_mop? current_mop : "dummy mood", num_admissible);
+ para_printf(&aca->pbout, "activated %s (%d admissible file%s)\n",
+ current_mop? current_mop : "dummy mood", num_admissible,
+ num_admissible == 1? "" : "s");
free_lpr:
lls_free_parse_result(aca->lpr, cmd);
return ret;
struct para_buffer *pb, void *data); \
extern struct osl_table *table_name ## _table;
+/** \cond blob_symbols */
DECLARE_BLOB_SYMBOLS(lyrics, lyr);
DECLARE_BLOB_SYMBOLS(images, img);
DECLARE_BLOB_SYMBOLS(moods, mood);
DECLARE_BLOB_SYMBOLS(playlists, pl);
+/** \endcond blob_symbols */
/** The columns of an abstract blob table. */
enum blob_table_columns {
/** The different sorting methods of the ls command. */
enum ls_sorting_method {
- /** -sp (default) */
- LS_SORT_BY_PATH,
- /** -ss */
- LS_SORT_BY_SCORE,
- /** -sl */
- LS_SORT_BY_LAST_PLAYED,
- /** -sn */
- LS_SORT_BY_NUM_PLAYED,
- /** -sf */
- LS_SORT_BY_FREQUENCY,
- /** -sc */
- LS_SORT_BY_CHANNELS,
- /** -si */
- LS_SORT_BY_IMAGE_ID,
- /** -sy */
- LS_SORT_BY_LYRICS_ID,
- /** -sb */
- LS_SORT_BY_BITRATE,
- /** -sd */
- LS_SORT_BY_DURATION,
- /** -sa */
- LS_SORT_BY_AUDIO_FORMAT,
- /** -sh */
- LS_SORT_BY_HASH,
+ LS_SORT_BY_PATH, /**< -s=p (default) */
+ LS_SORT_BY_SCORE, /**< -s=s */
+ LS_SORT_BY_LAST_PLAYED, /**< -s=l */
+ LS_SORT_BY_NUM_PLAYED, /**< -s=n */
+ LS_SORT_BY_FREQUENCY, /**< -s=f */
+ LS_SORT_BY_CHANNELS, /**< -s=c */
+ LS_SORT_BY_IMAGE_ID, /**< -s=i */
+ LS_SORT_BY_LYRICS_ID, /**< -s=y */
+ LS_SORT_BY_BITRATE, /**< -s=b */
+ LS_SORT_BY_DURATION, /**< -s=d */
+ LS_SORT_BY_AUDIO_FORMAT, /**< -s=a */
+ LS_SORT_BY_HASH, /**< -s=h */
};
/** The different listing modes of the ls command. */
enum ls_listing_mode {
- /** Default listing mode. */
- LS_MODE_SHORT,
- /** -l or -ll */
- LS_MODE_LONG,
- /** -lv */
- LS_MODE_VERBOSE,
- /** -lm */
- LS_MODE_MBOX,
- /** -lc */
- LS_MODE_CHUNKS,
- /** -lp */
- LS_MODE_PARSER,
+ LS_MODE_SHORT, /**< Default listing mode. */
+ LS_MODE_LONG, /**< -l or -l=l */
+ LS_MODE_VERBOSE, /** -l=v */
+ LS_MODE_MBOX, /** -l=m */
+ LS_MODE_CHUNKS, /** -l=c */
+ LS_MODE_PARSER, /** -l=p */
};
/**
WRITE_STATUS_ITEM(pb, SI_file_size, "%ld\n", statbuf.st_size / 1024);
}
+/**
+ * Deallocate and invalidate the status item strings.
+ *
+ * This needs to be a public function so that afs.c can call it on shutdown.
+ */
void free_status_items(void)
{
freep(&status_items);
freep(&parser_friendly_status_items);
}
-static int make_status_items(void)
+static void make_status_items(void)
{
const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
char *argv[] = {"ls", "--admissible", "--listing-mode=verbose"};
free_status_items();
if (!status_item_ls_data.path) /* no audio file open */
- return 0;
+ return;
ret = lls_parse(ARRAY_SIZE(argv), argv, cmd, &opts.lpr, NULL);
assert(ret >= 0);
time(¤t_time);
parser_friendly_status_items = pb.buf;
ret = 1;
out:
- if (ret < 0)
+ if (ret < 0) {
+ PARA_WARNING_LOG("could not create status items: %s\n",
+ para_strerror(-ret));
free_status_items();
+ }
lls_free_parse_result(opts.lpr, cmd);
- return ret;
}
/**
case SF_U8:
case SF_U16_LE:
case SF_U16_BE:
- return -E_AO_BAD_SAMPLE_FORMAT;
+ return -E_BAD_SAMPLE_FORMAT;
case SF_S8:
/* no need to set byte_format */
result->bits = 8;
if (info->type == AO_TYPE_FILE)
continue;
- PARA_DEBUG_LOG("%s: %s", info->short_name, info->name);
- PARA_DEBUG_LOG("priority: %d", info->priority);
+ PARA_DEBUG_LOG("name: %s: %s\n", info->short_name, info->name);
+ PARA_DEBUG_LOG("priority: %d\n", info->priority);
for (j = 0; j < info->option_count; j++) {
tmp = make_message("%s%s%s", keys? keys : "",
keys? ", " : "",
free(keys);
keys = tmp;
}
- PARA_DEBUG_LOG("keys: %s", keys? keys : "[none]");
+ PARA_DEBUG_LOG("keys: %s\n", keys? keys : "[none]");
free(keys);
- PARA_DEBUG_LOG("comment: %s", info->comment?
+ PARA_DEBUG_LOG("comment: %s\n", info->comment?
info->comment : "[none]");
}
}
return ret;
}
-__noreturn static void *aow_play(void *priv)
+static void *aow_play(void *priv)
{
struct writer_node *wn = priv;
struct private_aow_data *pawd = wn->private_data;
/** Define a pointer to an osl blob table with a canonical name. */
#define DEFINE_BLOB_TABLE_PTR(table_name) struct osl_table *table_name ## _table;
-
/** Define a blob table. */
#define INIT_BLOB_TABLE(table_name) \
DEFINE_BLOB_TABLE_DESC(table_name); \
para_printf(&aca->pbout, "cannot list %s\n", name);
return ret;
}
- id = *(uint32_t *)obj.data;
+ id = read_u32(obj.data);
para_printf(&aca->pbout, "%u\t%s\n", id, name);
return 1;
}
.action = print_blob,
};
int ret;
+
ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
pmd.lpr = aca->lpr;
assert(ret >= 0);
.data = &aca->fd,
.action = cat_blob
};
+
ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
assert(ret >= 0);
pmd.lpr = aca->lpr;
{
struct afs_callback_arg *aca = data;
int ret = osl(osl_del_row(table, row));
+
if (ret < 0) {
para_printf(&aca->pbout, "cannot remove %s\n", name);
return ret;
.data = aca,
.action = remove_blob
};
+
ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
assert(ret >= 0);
pmd.lpr = aca->lpr;
struct osl_object objs[NUM_BLOB_COLUMNS];
char *name = aca->query.data;
size_t name_len = strlen(name) + 1;
- uint32_t id;
+ uint32_t id = (uint32_t)-1; /* STFU, gcc */
+ char id_buf[sizeof(id)];
unsigned num_rows;
int ret;
if (ret < 0)
goto out;
if (!num_rows) { /* this is the first entry ever added */
- /* insert dummy row containing the id */
- id = 2; /* this entry will be entry #1, so 2 is the next */
- objs[BLOBCOL_ID].data = &id;
- objs[BLOBCOL_ID].size = sizeof(id);
+ /*
+ * Insert dummy row containing the next free ID. Since we are
+ * about to insert the first blob with ID 1, the next free ID
+ * will be 2.
+ */
+ id = 2U;
+ write_u32(id_buf, id);
+ objs[BLOBCOL_ID].data = id_buf;
+ objs[BLOBCOL_ID].size = sizeof(id_buf);
objs[BLOBCOL_NAME].data = "";
objs[BLOBCOL_NAME].size = 1;
objs[BLOBCOL_DEF].data = "";
ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
if (ret < 0)
goto out;
- id = *(uint32_t *)obj.data;
+ id = read_u32(obj.data);
obj.data = name + name_len;
obj.size = aca->query.size - name_len;
ret = osl(osl_update_object(table, row, BLOBCOL_DEF, &obj));
ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
if (ret < 0)
goto out;
- id = *(uint32_t *)obj.data + 1;
- obj.data = &id;
+ id = read_u32(obj.data) + 1;
+ write_u32(id_buf, id);
+ obj.data = &id_buf;
ret = osl(osl_update_object(table, row, BLOBCOL_ID, &obj));
if (ret < 0)
goto out;
}
id--;
- objs[BLOBCOL_ID].data = &id;
- objs[BLOBCOL_ID].size = sizeof(id);
+ write_u32(id_buf, id);
+ objs[BLOBCOL_ID].data = &id_buf;
+ objs[BLOBCOL_ID].size = sizeof(id_buf);
objs[BLOBCOL_NAME].data = name;
objs[BLOBCOL_NAME].size = name_len;
objs[BLOBCOL_DEF].data = name + name_len;
*
* This function is called from the addblob command handlers to instruct the
* afs process to store the input in a blob table. Input is read and decrypted
- * from the file descriptor given by cc and appended to arg_obj, which contains
+ * from the file descriptor given by cc and appended to a buffer which also contains
* the name of the blob to create. The combined buffer is made available to the
* afs process via the callback method.
*/
return blob_get_name_by_id(table_name ## _table, id, name); \
}
-
static int blob_get_def_by_name(struct osl_table *table, char *name,
struct osl_object *def)
{
{
struct osl_object obj;
int ret = osl(osl_get_object(table, row, BLOBCOL_NAME, &obj));
+
if (ret < 0)
return ret;
*name = obj.data;
const char *dir)
{
int ret;
+
desc->dir = dir;
ret = osl(osl_open_table(desc, table));
if (ret >= 0)
#include "buffer_tree.h"
#include "error.h"
#include "check_wav.h"
+#include "portable_io.h"
/** Length of a standard wav header. */
#define WAV_HEADER_LEN 44
PARA_INFO_LOG("found wav header\n");
cwc->state = CWS_HAVE_HEADER;
/* Only set those values which have not already been set. */
- cwc->channels = (unsigned)a[22];
- cwc->sample_rate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24);
- bps = a[34] + ((unsigned)a[35] << 8);
+ cwc->channels = a[22];
+ cwc->sample_rate = read_u32(a + 24);
+ bps = read_u16(a + 34);
if (bps != 8 && bps != 16) {
PARA_WARNING_LOG("%u bps not supported, assuming 16\n",
bps);
}
/**
- * Send a sideband packet through a blocking file descriptor.
+ * Receive a sideband packet from a blocking file descriptor.
*
* \param scc fd and crypto keys.
* \param expected_band The expected band designator.
me->loglevel = ret;
}
+/**
+ * Register functions to be called before and after a message is logged.
+ *
+ * \param pre_log_hook Called before the message is logged.
+ * \param post_log_hook Called after the message is logged.
+ *
+ * The purpose of this function is to provide a primitive for multi-threaded
+ * applications to serialize the access to the log facility, preventing
+ * interleaving log messages. This can be achieved by having the pre-log hook
+ * acquire a lock which blocks the other threads on the attempt to log a
+ * message at the same time. The post-log hook is responsible for releasing
+ * the lock.
+ *
+ * If these hooks are unnecessary, for example because the application is
+ * single-threaded, this function does not need to be called.
+ */
void daemon_set_hooks(void (*pre_log_hook)(void), void (*post_log_hook)(void))
{
me->pre_log_hook = pre_log_hook;
PARA_ERROR(AO_APPEND_OPTION, "ao append option: memory allocation failure"), \
PARA_ERROR(AO_BAD_DRIVER, "ao: invalid driver"), \
PARA_ERROR(AO_BAD_OPTION, "ao option is not of type key:value"), \
- PARA_ERROR(AO_BAD_SAMPLE_FORMAT, "ao: unsigned sample formats not supported"), \
PARA_ERROR(AO_DEFAULT_DRIVER, "ao: no usable output device"), \
PARA_ERROR(AO_EOF, "ao: end of file"), \
PARA_ERROR(AO_FILE_NOT_SUPP, "ao: file io drivers not supported"), \
enum para_error_codes {PARA_ERRORS};
#undef PARA_ERROR
#define PARA_ERROR(err, msg) msg
-/** Array of error strings. */
+/** All .c files need the declararation of the array of error strings. */
extern const char * const para_errlist[];
+/** Exactly one .c file per executable must define the array. */
#define DEFINE_PARA_ERRLIST const char * const para_errlist[] = {PARA_ERRORS}
/**
PARA_DEBUG_LOG("bn_size %d (0x%x)\n", bn_size, (unsigned)bn_size);
gret = gcry_mpi_scan(bn, GCRYMPI_FMT_STD, cp, bn_size, NULL);
if (gret) {
- PARA_ERROR_LOG("%s while scanning n\n",
+ PARA_ERROR_LOG("gcry_mpi_scan: %s\n",
gcry_strerror(gcry_err_code(gret)));
return-E_MPI_SCAN;
}
ci.argc = create_argv(ci.buffer, " ", &ci.argv);
ci.word_num = compute_word_num(ci.buffer, " ", ci.point);
+ /* determine the current word to complete */
end = ci.buffer + ci.point;
+
+ if (*end == ' ') {
+ if (ci.point == 0 || ci.buffer[ci.point - 1] == ' ') {
+ ci.word = para_strdup(NULL);
+ goto create_matches;
+ } else /* The cursor is positioned right after a word */
+ end--;
+ }
for (p = end; p > ci.buffer && *p != ' '; p--)
; /* nothing */
if (*p == ' ')
p++;
-
n = end - p + 1;
ci.word = para_malloc(n + 1);
strncpy(ci.word, p, n);
ci.word[n] = '\0';
-
+create_matches:
PARA_DEBUG_LOG("line: %s, point: %d (%c), wordnum: %d, word: %s\n",
ci.buffer, ci.point, ci.buffer[ci.point], ci.word_num, ci.word);
if (ci.word_num == 0)
[description]
para_play operates either in command mode or in insert mode. In insert
mode it presents a prompt and allows the user to enter commands like
- stop, play, pause etc. In command mode the current audio file and the
- playback position are shown and the program reads single key strokes
- from stdin. Keys may be mapped to commands so that the configured
- command is executed when a mapped key is pressed.
+ play, pause, quit, etc. In command mode the current audio file and the
+ playback position are shown instead of the prompt/command line, and
+ the program reads single key strokes from stdin. Keys may be mapped
+ to commands so that the configured command is executed whenever a
+ mapped key is pressed.
[/description]
m4_include(common-option-section.m4)
m4_include(help.m4)
arg_info = required_arg
arg_type = string
[help]
- If this is not given, the driver with the highest priority (see below)
- will be used.
+ If this is not given, the driver with the highest priority will be
+ used. The list of available drivers and their priorities is shown in
+ debug mode.
[/help]
[option ao-option]
short_opt = o
[help]
For each time this option is given, the supplied key-value pair is
appended to the list of options for the driver. Invalid keys are
- silently ignored.
+ silently ignored. The list of available keys is shown in debug mode.
[/help]
[subcommand oss]
purpose = output plugin for the Open Sound System
arg_info = required_arg
arg_type = string
default_val = /dev/dsp
-[subcommand osx]
- purpose = output plugin for Mac OS coreaudio
- [option numbuffers]
- short_opt = n
- summary = number of audio buffers to allocate
- typestr = num
- arg_info = required_arg
- arg_type = uint32
- default_val = 20
- [help]
- Increase if you get buffer underruns.
- [/help]
[subcommand file]
purpose = output plugin that writes to a local file
[option filename]
/* Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
-/** \file mixer.c A volume fader and alarm clock for OSS. */
+/** \file mixer.c A volume fader and alarm clock. */
#include <regex.h>
#include <lopsub.h>
}
if (ret < 0)
goto close_mixer;
- ret = (*(mixer_subcommand_handler_t *)(lls_user_data(cmd)))(m ,h);
+ ret = (*(mixer_subcommand_handler_t *)(lls_user_data(cmd)))(m, h);
close_mixer:
m->close(&h);
free_sub_lpr:
};
static const int frame_size_index[] = {24000, 72000, 72000};
-static const char *mode_text[] = {"stereo", "joint stereo", "dual channel", "mono", "invalid"};
-
#ifdef HAVE_ID3TAG
#include <id3tag.h>
return frequencies[h->version][h->freq];
}
-static const char *header_mode(struct mp3header *h)
+static const char *header_mode(const struct mp3header *h)
{
- if (h->mode > 4)
- h->mode = 4; /* invalid */
+ const char * const mode_text[] = {"stereo", "joint stereo",
+ "dual channel", "mono"};
+
+ if (h->mode >= ARRAY_SIZE(mode_text))
+ return "invalid";
return mode_text[h->mode];
}
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
-
-/* At least NetBSD needs these. */
-#ifndef AI_V4MAPPED
-#define AI_V4MAPPED 0
-#endif
-#ifndef AI_ALL
-#define AI_ALL 0
-#endif
-#ifndef AI_ADDRCONFIG
-#define AI_ADDRCONFIG 0
-#endif
-
#include <regex.h>
#include "error.h"
* The first call to this function tries to bind a socket to the abstract name
* space. The result of this test is stored in a static variable. Subsequent
* calls read this variable and create abstract sockets on systems that support
- * them.
+ * them. If a NULL pointer is passed as the name, the function only
+ * initializes the static variable.
*/
static int init_unix_addr(struct sockaddr_un *u, const char *name)
{
static int use_abstract;
- if (strlen(name) + 1 >= UNIX_PATH_MAX)
- return -E_NAME_TOO_LONG;
memset(u->sun_path, 0, UNIX_PATH_MAX);
u->sun_family = PF_UNIX;
if (use_abstract == 0) { /* executed only once */
PARA_NOTICE_LOG("%susing abstract socket namespace\n",
use_abstract == 1? "" : "not ");
}
+ if (!name)
+ return 0;
+ if (strlen(name) + 1 >= UNIX_PATH_MAX)
+ return -E_NAME_TOO_LONG;
strcpy(u->sun_path + (use_abstract == 1? 1 : 0), name);
return 1;
}
int fd, ret;
ret = init_unix_addr(&unix_addr, name);
- if (ret < 0)
+ if (ret <= 0) /* error, or name was NULL */
return ret;
ret = socket(PF_UNIX, SOCK_STREAM, 0);
if (ret < 0)
return -E_BIGNUM;
ret = read_bignum(p, end - p, &e);
if (ret < 0)
- goto fail;
+ goto free_rsa;
p += ret;
ret = read_bignum(p, end - p, &n);
if (ret < 0)
- goto fail;
+ goto free_e;
#ifdef HAVE_RSA_SET0_KEY
RSA_set0_key(rsa, n, e, NULL);
#else
#endif
*result = rsa;
return 1;
-fail:
+free_e:
+ BN_free(e);
+free_rsa:
RSA_free(rsa);
return ret;
}
case SF_S16_BE: return AFMT_S16_BE;
case SF_U16_LE: return AFMT_U16_LE;
case SF_U16_BE: return AFMT_U16_BE;
- default: return AFMT_S16_LE;
+ default: return -E_BAD_SAMPLE_FORMAT;
}
}
if (ret < 0)
goto err;
/* set PCM format */
- sample_format = format = get_oss_format(sample_format);
+ ret = get_oss_format(sample_format);
+ if (ret < 0)
+ return ret;
+ sample_format = format = ret;
ret = ioctl(powd->fd, SNDCTL_DSP_SETFMT, &format);
if (ret < 0) {
ret = -ERRNO_TO_PARA_ERROR(errno);
+ PARA_ERROR_LOG("could not set sample format\n");
goto err;
}
ret = -E_BAD_SAMPLE_FORMAT;
.post_select = command_post_select,
.context = sct,
}, &sched);
+ /*
+ * Detect whether the abstract Unix domain socket space is supported,
+ * but do not create the socket. We check this once in server context
+ * so that the command handlers inherit this bit of information and
+ * don't need to check again.
+ */
+ create_local_socket(NULL);
return;
err:
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
#include <speex/speex_callbacks.h>
#include "para.h"
+#include "portable_io.h"
#include "list.h"
#include "sched.h"
#include "buffer_tree.h"
return 1;
}
-#if !defined(__LITTLE_ENDIAN__) && ( defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) )
-#define le_short(s) ((short) ((unsigned short) (s) << 8) | ((unsigned short) (s) >> 8))
-#else
-#define le_short(s) ((short) (s))
-#endif
-
/**
* Size of the output buffer.
*
samples = new_frame_size * psd->shi.channels;
btr_output = para_malloc(2 * samples);
for (i = 0; i < samples; i++)
- btr_output[i] = le_short(output[i + skip_idx]);
+ btr_output[i] = read_u16(output + i + skip_idx);
btr_add_output((char *)btr_output, samples * 2, btrn);
}
return 1;
}
/**
- * Print a formated message to a dynamically allocated string.
+ * Print a formatted message to a dynamically allocated string.
*
- * \param result The formated string is returned here.
+ * \param result The formatted string is returned here.
* \param fmt The format string.
* \param ap Initialized list of arguments.
*
missing_executables="$result"
if [[ -z "$missing_objects" && -z "$missing_executables" ]]; then
- ssh-keygen -q -t rsa -b 2048 -N "" -f $privkey
+ ssh-keygen -q -t rsa -b 2048 -N "" -m PEM -f $privkey
key_gen_result=$?
read &>/dev/null < /dev/tcp/localhost/$port
#include "fd.h"
extern struct misc_meta_data *mmd;
-
-extern void dccp_send_init(struct sender *);
-extern void http_send_init(struct sender *);
-extern void udp_send_init(struct sender *);
-
extern const struct sender udp_sender, dccp_sender, http_sender;
const struct sender * const senders[] = {
&http_sender, &dccp_sender, &udp_sender, NULL};
}
#ifndef MAP_POPULATE
+/** As of 2018, neither FreeBSD-11.2 nor NetBSD-8.0 have MAP_POPULATE. */
#define MAP_POPULATE 0
#endif
</ul>
<b> Author: </b> André Noll,
-<a href="mailto:maan@tuebingen.mpg.de">maan@tuebingen.mpg.de</a>
+<a href="mailto:maan@tuebingen.mpg.de">maan@tuebingen.mpg.de</a>,
+Homepage: <a href="http://people.tuebingen.mpg.de/maan/">http://people.tuebingen.mpg.de/maan/</a>
<br>
Comments and bug reports are welcome. Please provide the version of
paraslash you are using and relevant parts of the logs.
Next, change to the "bar" account on client_host and generate the
key pair with the commands
- ssh-keygen -q -t rsa -b 2048 -N '' -f $key
+ ssh-keygen -q -t rsa -b 2048 -N '' -m PEM
This generates the two files id_rsa and id_rsa.pub in ~/.ssh. Note
that para_server won't accept keys shorter than 2048 bits. Moreover,