clixon/apps/backend/backend_client.c
2016-02-22 22:17:30 +01:00

1066 lines
27 KiB
C

/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLICON.
CLICON is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
CLICON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CLICON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <netinet/in.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clicon/clicon.h>
#include "backend_commit.h"
#include "backend_lock.h"
#include "backend_plugin.h"
#include "backend_client.h"
#include "backend_handle.h"
/*! Add client notification subscription. Ie send notify to this client when event occurs
* @param[in] ce Client entry struct
* @param[in] stream Notification stream name
* @param[in] format How to display event (see enum format_enum)
* @param[in] filter Filter, what to display, eg xpath for format=xml, fnmatch
*
* @see backend_notify - where subscription is made and notify call is made
*/
static struct client_subscription *
client_subscription_add(struct client_entry *ce,
char *stream,
enum format_enum format,
char *filter)
{
struct client_subscription *su = NULL;
if ((su = malloc(sizeof(*su))) == NULL){
clicon_err(OE_PLUGIN, errno, "malloc");
goto done;
}
memset(su, 0, sizeof(*su));
su->su_stream = strdup(stream);
su->su_format = format;
su->su_filter = strdup(filter);
su->su_next = ce->ce_subscription;
ce->ce_subscription = su;
done:
return su;
}
static struct client_entry *
ce_find_bypid(struct client_entry *ce_list, int pid)
{
struct client_entry *ce;
for (ce = ce_list; ce; ce = ce->ce_next)
if (ce->ce_pid == pid)
return ce;
return NULL;
}
static int
client_subscription_delete(struct client_entry *ce,
struct client_subscription *su0)
{
struct client_subscription *su;
struct client_subscription **su_prev;
su_prev = &ce->ce_subscription; /* this points to stack and is not real backpointer */
for (su = *su_prev; su; su = su->su_next){
if (su == su0){
*su_prev = su->su_next;
free(su->su_stream);
if (su->su_filter)
free(su->su_filter);
free(su);
break;
}
su_prev = &su->su_next;
}
return 0;
}
static struct client_subscription *
client_subscription_find(struct client_entry *ce, char *stream)
{
struct client_subscription *su = NULL;
for (su = ce->ce_subscription; su; su = su->su_next)
if (strcmp(su->su_stream, stream) == 0)
break;
return su;
}
/*! Remove client entry state
* Close down everything wrt clients (eg sockets, subscriptions)
* Finally actually remove client struct in handle
* @see backend_client_delete
*/
int
backend_client_rm(clicon_handle h,
struct client_entry *ce)
{
struct client_entry *c;
struct client_entry *c0;
struct client_entry **ce_prev;
struct client_subscription *su;
c0 = backend_client_list(h);
ce_prev = &c0; /* this points to stack and is not real backpointer */
for (c = *ce_prev; c; c = c->ce_next){
if (c == ce){
if (ce->ce_s){
event_unreg_fd(ce->ce_s, from_client);
close(ce->ce_s);
ce->ce_s = 0;
}
while ((su = ce->ce_subscription) != NULL)
client_subscription_delete(ce, su);
break;
}
ce_prev = &c->ce_next;
}
return backend_client_delete(h, ce); /* actually purge it */
}
/*! Internal message: Change entry set/delete in database xmldb variant
* @param[in] h Clicon handle
* @param[in] s Socket where request arrived, and where replies are sent
* @param[in] pid Unix process id
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_change(clicon_handle h,
int s,
int pid,
struct clicon_msg *msg,
const char *label)
{
int retval = -1;
uint32_t len;
char *xk;
char *dbname;
enum operation_type op;
char *str = NULL;
char *candidate_db;
char *val=NULL;
yang_spec *yspec;
if (clicon_msg_change_decode(msg,
&dbname,
&op,
&xk,
&val,
&len,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if ((candidate_db = clicon_candidate_db(h)) == NULL){
send_msg_err(s, 0, 0, "candidate db not set");
goto done;
}
/* candidate is locked by other client */
if (strcmp(dbname, candidate_db) == 0 &&
db_islocked(h) &&
pid != db_islocked(h)){
send_msg_err(s, OE_DB, 0,
"lock failed: locked by %d", db_islocked(h));
goto done;
}
/* Update database */
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_XML, 0, "yang spec not found");
goto done;
}
if (xmldb_put_xkey(dbname, xk, val, yspec, op) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if (send_msg_ok(s) < 0)
goto done;
retval = 0;
done:
if (str)
free(str);
return retval;
}
/*! Internal message: Change entries as XML
* @param[in] h Clicon handle
* @param[in] s Socket where request arrived, and where replies are sent
* @param[in] pid Unix process id
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_xmlput(clicon_handle h,
int s,
int pid,
struct clicon_msg *msg,
const char *label)
{
int retval = -1;
char *dbname;
enum operation_type op;
cvec *cvv = NULL;
char *str = NULL;
char *xml = NULL;
yang_spec *ys;
char *candidate_db;
cxobj *xt;
if ((ys = clicon_dbspec_yang(h)) == NULL)
goto done;
if (clicon_msg_xmlput_decode(msg,
&dbname,
&op,
&xml,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if ((candidate_db = clicon_candidate_db(h)) == NULL){
send_msg_err(s, 0, 0, "candidate db not set");
goto done;
}
/* candidate is locked by other client */
if (strcmp(dbname, candidate_db) == 0 &&
db_islocked(h) &&
pid != db_islocked(h)){
send_msg_err(s, OE_DB, 0,
"lock failed: locked by %d", db_islocked(h));
goto done;
}
if (clicon_xml_parse_string(&xml, &xt) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if (xmldb_put(dbname, xt, ys, op) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if (send_msg_ok(s) < 0)
goto done;
retval = 0;
done:
if (str)
free(str);
if (cvv)
cvec_free (cvv);
return retval;
}
/* Nr of snapshots. Can be made into a dynamic option */
#define SNAPSHOTS_NR 30
/*! dump old running_db to snapshot file #0. move all other checkpoints
* one step up
*/
int
config_snapshot(clicon_handle h,
char *dbname,
char *dir)
{
int retval = -1;
char filename0[MAXPATHLEN];
char filename1[MAXPATHLEN];
struct stat st;
int i;
FILE *f = NULL;
cxobj *xn;
yang_spec *yspec = clicon_dbspec_yang(h);
if (stat(dir, &st) < 0){
clicon_err(OE_CFG, errno, "%s: stat(%s): %s\n",
__FUNCTION__, dir, strerror(errno));
return -1;
}
if (!S_ISDIR(st.st_mode)){
clicon_err(OE_CFG, 0, "%s: %s: not directory\n",
__FUNCTION__, dir);
return -1;
}
for (i=SNAPSHOTS_NR-1; i>0; i--){
snprintf(filename0, MAXPATHLEN, "%s/%d",
dir,
i-1);
snprintf(filename1, MAXPATHLEN, "%s/%d",
dir,
i);
if (stat(filename0, &st) == 0)
if (rename(filename0, filename1) < 0){
clicon_err(OE_CFG, errno, "%s: rename(%s, %s): %s\n",
__FUNCTION__, filename0, filename1, strerror(errno));
return -1;
}
}
/* Make the most current snapshot */
snprintf(filename0, MAXPATHLEN, "%s/0", dir);
if ((f = fopen(filename0, "wb")) == NULL){
clicon_err(OE_CFG, errno, "Creating file %s", filename0);
return -1;
}
if (xmldb_get(dbname, "/", yspec, &xn) < 0)
goto done;
if (clicon_xml2file(f, xn, 0, 1) < 0)
goto done;
retval = 0;
done:
if (f != NULL)
fclose(f);
if (xn)
xml_free(xn);
return retval;
}
/*! Internal message: Dump/print database to file
* @param[in] h Clicon handle
* @param[in] s Socket where request arrived, and where replies are sent
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_save(clicon_handle h,
int s,
struct clicon_msg *msg,
const char *label)
{
int retval = -1;
char *filename;
char *archive_dir;
char *db;
uint32_t snapshot;
FILE *f = NULL;
cxobj *xn = NULL;
yang_spec *yspec;
if (clicon_msg_save_decode(msg,
&db,
&snapshot,
&filename,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if (snapshot){
if ((archive_dir = clicon_archive_dir(h)) == NULL){
clicon_err(OE_PLUGIN, 0, "snapshot set and clicon_archive_dir not defined");
goto done;
}
if (config_snapshot(h, db, archive_dir) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
}
else{
if ((f = fopen(filename, "wb")) == NULL){
clicon_err(OE_CFG, errno, "Creating file %s", filename);
return -1;
}
yspec = clicon_dbspec_yang(h);
if (xmldb_get(db, "/", yspec, &xn) < 0)
goto done;
if (clicon_xml2file(f, xn, 0, 1) < 0)
goto done;
}
if (send_msg_ok(s) < 0)
goto done;
retval = 0;
done:
if (f != NULL)
fclose(f);
if (xn)
xml_free(xn);
return retval;
}
/*! Internal message: Load file into database
* @param[in] h Clicon handle
* @param[in] s Socket where request arrived, and where replies are sent
* @param[in] pid Unix process id
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_load(clicon_handle h,
int s,
int pid,
struct clicon_msg *msg,
const char *label)
{
char *filename = NULL;
int retval = -1;
char *dbname = NULL;
int replace = 0;
char *candidate_db;
int fd = -1;
cxobj *xt = NULL;
cxobj *xn;
yang_spec *yspec;
if (clicon_msg_load_decode(msg,
&replace,
&dbname,
&filename,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if ((candidate_db = clicon_candidate_db(h)) == NULL){
send_msg_err(s, 0, 0, "candidate db not set");
goto done;
}
/* candidate is locked by other client */
if (strcmp(dbname, candidate_db) == 0 &&
db_islocked(h) &&
pid != db_islocked(h)){
send_msg_err(s, OE_DB, 0, "lock failed: locked by %d", db_islocked(h));
goto done;
}
if (replace){
if (unlink(dbname) < 0){
send_msg_err(s, OE_UNIX, 0, "rm %s %s", filename, strerror(errno));
goto done;
}
if (db_init(dbname) < 0)
goto done;
}
if ((fd = open(filename, O_RDONLY)) < 0){
clicon_err(OE_UNIX, errno, "%s: open(%s)", __FUNCTION__, filename);
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if (clicon_xml_parse_file(fd, &xt, "</clicon>") < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
yspec = clicon_dbspec_yang(h);
if ((xn = xml_child_i(xt, 0)) != NULL){
if (xmldb_put(dbname, xn, yspec, replace?OP_REPLACE:OP_MERGE) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
}
if (send_msg_ok(s) < 0)
goto done;
retval = 0;
done:
if (fd != -1)
close(fd);
if (xt)
xml_free(xt);
return retval;
}
/*! Internal message: Initialize database
* @param[in] h Clicon handle
* @param[in] s Socket where request arrived, and where replies are sent
* @param[in] pid Unix process id
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_initdb(clicon_handle h,
int s,
int pid,
struct clicon_msg *msg,
const char *label)
{
char *filename1;
int retval = -1;
char *candidate_db;
if (clicon_msg_initdb_decode(msg,
&filename1,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if ((candidate_db = clicon_candidate_db(h)) == NULL){
send_msg_err(s, 0, 0, "candidate db not set");
goto done;
}
/* candidate is locked by other client */
if (strcmp(filename1, candidate_db) == 0 &&
db_islocked(h) &&
pid != db_islocked(h)){
send_msg_err(s, OE_DB, 0, "lock failed: locked by %d", db_islocked(h));
goto done;
}
if (db_init(filename1) < 0)
goto done;
/* Change mode if shared candidate. XXXX full rights for all is no good */
if (strcmp(filename1, candidate_db) == 0)
chmod(filename1, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
if (send_msg_ok(s) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Internal message: Remove file
* @param[in] h Clicon handle
* @param[in] s Socket where request arrived, and where replies are sent
* @param[in] pid Unix process id
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_rm(clicon_handle h,
int s,
int pid,
struct clicon_msg *msg,
const char *label)
{
char *filename1;
int retval = -1;
char *candidate_db;
if (clicon_msg_rm_decode(msg,
&filename1,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if ((candidate_db = clicon_candidate_db(h)) == NULL){
send_msg_err(s, 0, 0, "candidate db not set");
goto done;
}
/* candidate is locked by other client */
if (strcmp(filename1, candidate_db) == 0 &&
db_islocked(h) &&
pid != db_islocked(h)){
send_msg_err(s, OE_DB, 0, "lock failed: locked by %d", db_islocked(h));
goto done;
}
if (unlink(filename1) < 0){
send_msg_err(s, OE_UNIX, 0, "rm %s %s", filename1, strerror(errno));
goto done;
}
if (send_msg_ok(s) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Internal message: Copy file from file1 to file2
* @param[in] h Clicon handle
* @param[in] s Socket where request arrived, and where replies are sent
* @param[in] pid Unix process id
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_copy(clicon_handle h,
int s,
int pid,
struct clicon_msg *msg,
const char *label)
{
char *filename1;
char *filename2;
int retval = -1;
char *candidate_db;
if (clicon_msg_copy_decode(msg,
&filename1,
&filename2,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if ((candidate_db = clicon_candidate_db(h)) == NULL){
send_msg_err(s, 0, 0, "candidate db not set");
goto done;
}
/* candidate is locked by other client */
if (strcmp(filename2, candidate_db) == 0 &&
db_islocked(h) &&
pid != db_islocked(h)){
send_msg_err(s, OE_DB, 0, "lock failed: locked by %d", db_islocked(h));
goto done;
}
if (file_cp(filename1, filename2) < 0){
send_msg_err(s, OE_UNIX, errno, "copy %s to %s", filename1, filename2);
goto done;
}
/* Change mode if shared candidate. XXXX full rights for all is no good */
if (strcmp(filename2, candidate_db) == 0)
chmod(filename2, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
if (send_msg_ok(s) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Internal message: Lock database
* @param[in] h Clicon handle
* @param[in] s Client socket where request arrived, and where replies are sent
* @param[in] pid Client unix process id
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_lock(clicon_handle h,
int s,
int pid,
struct clicon_msg *msg,
const char *label)
{
char *db;
int retval = -1;
char *candidate_db;
if (clicon_msg_lock_decode(msg,
&db,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if ((candidate_db = clicon_candidate_db(h)) == NULL){
send_msg_err(s, 0, 0, "candidate db not set");
goto done;
}
if (strcmp(db, candidate_db)){
send_msg_err(s, OE_DB, 0, "can not lock %s, only %s",
db, candidate_db);
goto done;
}
if (db_islocked(h)){
if (pid == db_islocked(h))
;
else{
send_msg_err(s, OE_DB, 0, "lock failed: locked by %d", db_islocked(h));
goto done;
}
}
else
db_lock(h, pid);
if (send_msg_ok(s) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Internal message: Unlock database
* @param[in] h Clicon handle
* @param[in] s Client socket where request arrived, and where replies are sent
* @param[in] pid Client unix process id
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_unlock(clicon_handle h,
int s,
int pid,
struct clicon_msg *msg,
const char *label)
{
char *db;
int retval = -1;
char *candidate_db;
if (clicon_msg_unlock_decode(msg,
&db,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if ((candidate_db = clicon_candidate_db(h)) == NULL){
send_msg_err(s, 0, 0, "candidate db not set");
goto done;
}
if (strcmp(db, candidate_db)){
send_msg_err(s, OE_DB, 0, "can not unlock %s, only %s",
db, clicon_candidate_db(h));
goto done;
}
if (db_islocked(h)){
if (pid == db_islocked(h))
db_unlock(h);
else{
send_msg_err(s, OE_DB, 0, "unlock failed: locked by %d", db_islocked(h));
goto done;
}
}
if (send_msg_ok(s) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Internal message: Kill session (Kill the process)
* @param[in] h Clicon handle
* @param[in] s Client socket where request arrived, and where replies are sent
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_kill(clicon_handle h,
int s,
struct clicon_msg *msg,
const char *label)
{
uint32_t pid; /* other pid */
int retval = -1;
struct client_entry *ce;
if (clicon_msg_kill_decode(msg,
&pid,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
/* may or may not be in active client list, probably not */
if ((ce = ce_find_bypid(backend_client_list(h), pid)) != NULL)
backend_client_rm(h, ce);
if (kill (pid, 0) != 0 && errno == ESRCH) /* Nothing there */
;
else{
killpg(pid, SIGTERM);
kill(pid, SIGTERM);
#if 0 /* Hate sleeps we assume it died, see also 0 in next if.. */
sleep(1);
#endif
}
if (1 || (kill (pid, 0) != 0 && errno == ESRCH)){ /* Nothing there */
/* clear from locks */
if (db_islocked(h) == pid)
db_unlock(h);
}
else{ /* failed to kill client */
send_msg_err(s, OE_DB, 0, "failed to kill %d", pid);
goto done;
}
if (send_msg_ok(s) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Internal message: Set debug level. This is global, not just for the session.
* @param[in] h Clicon handle
* @param[in] s Client socket where request arrived, and where replies are sent
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_debug(clicon_handle h,
int s,
struct clicon_msg *msg,
const char *label)
{
int retval = -1;
uint32_t level;
if (clicon_msg_debug_decode(msg,
&level,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
clicon_debug_init(level, NULL); /* 0: dont debug, 1:debug */
if (send_msg_ok(s) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Internal message: downcall backend plugin
* @param[in] h Clicon handle
* @param[in] s Client socket where request arrived, and where replies are sent
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_call(clicon_handle h,
int s,
struct clicon_msg *msg,
const char *label)
{
int retval = -1;
void *reply_data = NULL;
uint16_t reply_data_len = 0;
struct clicon_msg_call_req *req;
if (clicon_msg_call_decode(msg, &req, label) < 0) {
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
#ifdef notyet
if (!strlen(req->cr_plugin)) /* internal */
internal_function(req, &reply_data_len, &reply_data);
else
#endif
if (plugin_downcall(h, req, &reply_data_len, &reply_data) < 0) {
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
retval = send_msg_reply(s,CLICON_MSG_OK, (char *)reply_data, reply_data_len);
free(reply_data);
done:
return retval;
}
/*! Internal message: Create subscription for notifications
* @param[in] h Clicon handle
* @param[in] ce Client entry (from).
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_subscription(clicon_handle h,
struct client_entry *ce,
struct clicon_msg *msg,
const char *label)
{
int status;
enum format_enum format;
char *stream;
char *filter;
int retval = -1;
struct client_subscription *su;
clicon_log_notify_t *old;
if (clicon_msg_subscription_decode(msg,
&status,
&stream,
&format,
&filter,
label) < 0){
send_msg_err(ce->ce_s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if (status){
if ((su = client_subscription_add(ce, stream, format, filter)) == NULL){
send_msg_err(ce->ce_s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
}
else{
if ((su = client_subscription_find(ce, stream)) != NULL)
client_subscription_delete(ce, su);
}
/* Avoid recursion when sending logs */
old = clicon_log_register_callback(NULL, NULL);
if (send_msg_ok(ce->ce_s) < 0)
goto done;
clicon_log_register_callback(old, h); /* XXX: old h */
retval = 0;
done:
return retval;
}
/*! An internal clicon message has arrived from a client. Receive and dispatch.
* @param[in] s Socket where message arrived. read from this.
* @param[in] arg Client entry (from).
* @retval 0 OK
* @retval -1 Error Terminates backend and is never called). Instead errors are
* propagated back to client.
*/
int
from_client(int s, void* arg)
{
struct client_entry *ce = (struct client_entry *)arg;
clicon_handle h = ce->ce_handle;
struct clicon_msg *msg;
enum clicon_msg_type type;
int eof;
assert(s == ce->ce_s);
if (clicon_msg_rcv(ce->ce_s, &msg, &eof, __FUNCTION__) < 0)
goto done;
if (eof){
backend_client_rm(h, ce);
goto done;
}
type = ntohs(msg->op_type);
switch (type){
case CLICON_MSG_COMMIT:
if (from_client_commit(h, ce->ce_s, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_VALIDATE:
if (from_client_validate(h, ce->ce_s, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_CHANGE:
if (from_client_change(h, ce->ce_s, ce->ce_pid, msg,
(char *)__FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_XMLPUT:
if (from_client_xmlput(h, ce->ce_s, ce->ce_pid, msg,
(char *)__FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_SAVE:
if (from_client_save(h, ce->ce_s, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_LOAD:
if (from_client_load(h, ce->ce_s, ce->ce_pid, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_RM:
if (from_client_rm(h, ce->ce_s, ce->ce_pid, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_INITDB:
if (from_client_initdb(h, ce->ce_s, ce->ce_pid, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_COPY:
if (from_client_copy(h, ce->ce_s, ce->ce_pid, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_LOCK:
if (from_client_lock(h, ce->ce_s, ce->ce_pid, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_UNLOCK:
if (from_client_unlock(h, ce->ce_s, ce->ce_pid, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_KILL:
if (from_client_kill(h, ce->ce_s, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_DEBUG:
if (from_client_debug(h, ce->ce_s, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_CALL:
if (from_client_call(h, ce->ce_s, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_SUBSCRIPTION:
if (from_client_subscription(h, ce, msg, __FUNCTION__) < 0)
goto done;
break;
default:
send_msg_err(s, OE_PROTO, 0, "Unexpected message: %d", type);
goto done;
}
// retval = 0;
done:
unchunk_group(__FUNCTION__);
// return retval;
return 0; // -1 here terminates
}