Inital commit

This commit is contained in:
Olof hagsand 2016-02-22 22:17:30 +01:00
parent edc5e091bb
commit d6e393ea58
145 changed files with 58117 additions and 0 deletions

73
lib/Makefile.in Normal file
View file

@ -0,0 +1,73 @@
#
# Makefile
#
# 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/>.
#
#
VPATH = @srcdir@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
CC = @CC@
CFLAGS = @CFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
SHELL = /bin/sh
SUBDIRS = src clicon
.PHONY: all clean depend $(SUBDIRS) install
all: $(SUBDIRS)
depend:
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) depend); done
$(SUBDIRS):
(cd $@; $(MAKE) $(MFLAGS) all)
install:
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
install-include:
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done; \
(cd clicon; $(MAKE) $(MFLAGS) $@)
uninstall:
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
config.status: configure
$(SHELL) config.status --recheck
clean:
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) clean); done
distclean: clean
rm -f Makefile *~ .depend
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) distclean); done; \
(cd clicon; $(MAKE) $(MFLAGS) $@)
tags:
find $(srcdir) -name '*.[chyl]' -print | etags -

44
lib/clicon/Makefile.in Normal file
View file

@ -0,0 +1,44 @@
#
# 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/>.
#
prefix = @prefix@
includedir = @includedir@
.PHONY: install-include
all:
depend:
install:
install-include:
install -m 755 -d $(DESTDIR)$(includedir)/clicon
install -m 644 *.h $(DESTDIR)$(includedir)/clicon
uninstall:
rm -f $(includedir)/clicon
clean:
distclean:
rm -f Makefile clicon.h

78
lib/clicon/clicon.h.in Normal file
View file

@ -0,0 +1,78 @@
/*
*
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/>.
* Meta-include file that includes all sub-files in control-lib
* Note: this include files is for external purposes. Do not include this
* file in clicon lib-routines.
*/
/* This include file requires the following include file dependencies */
#include <stdio.h>
#include <stdint.h>
#include <dirent.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
/*
* CLICON version macros
*/
#undef CLICON_VERSION_STRING
#undef CLICON_VERSION_MAJOR
#undef CLICON_VERSION_MINOR
#undef CLICON_VERSION_PATCH
/*
* Use this constant to disable some prototypes that should not be visible outside the lib.
* This is an alternative to use separate internal include files.
*/
#define LIBCLICON_API 1
#include <clicon/clicon_sig.h>
#include <clicon/clicon_log.h>
#include <clicon/clicon_err.h>
#include <clicon/clicon_queue.h>
#include <clicon/clicon_hash.h>
#include <clicon/clicon_handle.h>
#include <clicon/clicon_qdb.h>
#include <clicon/clicon_yang.h>
#include <clicon/clicon_yang_type.h>
#include <clicon/clicon_chunk.h>
#include <clicon/clicon_event.h>
#include <clicon/clicon_string.h>
#include <clicon/clicon_file.h>
#include <clicon/clicon_xml.h>
#include <clicon/clicon_proto.h>
#include <clicon/clicon_proto_encode.h>
#include <clicon/clicon_proto_client.h>
#include <clicon/clicon_proc.h>
#include <clicon/clicon_options.h>
#include <clicon/clicon_xml_map.h>
#include <clicon/clicon_xml_db.h>
#include <clicon/clicon_xsl.h>
#include <clicon/clicon_plugin.h>
#include <clicon/clicon_plugin.h>
/*
* Global variables generated by Makefile
*/
extern const char CLICON_BUILDSTR[];
extern const char CLICON_VERSION[];

166
lib/clicon/clicon_chunk.h Normal file
View file

@ -0,0 +1,166 @@
/*
*
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/>.
*
* Copyright (C) 2002 Benny Holmgren, All rights reserved
*/
#ifndef _CLICON_CHUNK_H_
#define _CLICON_CHUNK_H_
/*
* Compile with chunk diagnostics. XXX Should be in Makefile.in ??
*/
#define CHUNK_DIAG
/*
* Base number of bits to shift getting the size of a chunk_head.
*/
#define CHUNK_BASE 6
/*
* Number of predefined chunk sizes. I.e. the number of chunk heads in the
* chunk_heads vector.
*/
#define CHUNK_HEADS (32 - CHUNK_BASE)
#ifdef CHUNK_DIAG
/*
* Chunk diagnostics
*/
typedef struct _chunk_diag_t {
const char *cd_file; /* File which requested chunk */
int cd_line; /* Line in requesting file */
} chunk_diag_t;
#endif /* CHUNK_DIAG */
/*
* The block header.
*/
struct _chunk_head_t;
typedef struct _chunk_head_t chunk_head_t;
typedef struct _chunk_block_t {
qelem_t cb_qelem; /* Circular queue of blocks */
chunk_head_t *cb_head; /* The chunk head I belong to */
void *cb_blk; /* Allocated memory block */
uint16_t cb_ref; /* Number of used chunks of block */
} chunk_block_t;
/*
* The chunk header.
*/
struct _chunk_grpent_t;
typedef struct _chunk_grpent_t chunk_grpent_t;
typedef struct _chunk_t {
qelem_t c_qelem; /* Circular queue of chunks */
chunk_block_t *c_blk; /* The block I belong to */
#ifdef CHUNK_DIAG
chunk_diag_t c_diag; /* The diagnostics structure */
#endif /* CHUNK_DIAG */
chunk_grpent_t *c_grpent;
} chunk_t;
/*
* The head of a chunk size. Each predefined size has it's own head keeping
* track of all blocks and chunks for the size.
*/
struct _chunk_head_t {
size_t ch_size; /* Chunk size */
int ch_nchkperblk; /* Number pf chunks per block */
size_t ch_blksz; /* Size of a block */
int ch_nblks; /* Number of allocated blocks */
chunk_block_t *ch_blks; /* Circular list of blocks */
chunk_t *ch_cnks; /* Circular list of chunks in use */
size_t ch_nfree; /* Number of free chunks */
chunk_t *ch_free; /* Circular list of free chunks */
};
/*
* The chunk group structure.
*/
typedef struct _chunk_group_t {
qelem_t cg_qelem; /* List of chunk groups */
char *cg_name; /* Name of group */
chunk_grpent_t *cg_ent; /* List of chunks in the group */
} chunk_group_t;
/*
* The chunk group entry structure.
*/
struct _chunk_grpent_t {
qelem_t ce_qelem; /* Circular list of entries */
chunk_group_t *ce_grp; /* The group I belong to */
chunk_t *ce_cnk; /* Pointer to the chunk */
};
/*
* Public function declarations
*/
#ifdef CHUNK_DIAG
void *_chunk (size_t, const char *, const char *, int);
#define chunk(siz,label) _chunk((siz),(label),__FILE__,__LINE__)
void *_rechunk (void *, size_t, const char *, const char *, int);
#define rechunk(ptr,siz,label) _rechunk((ptr),(siz),(label),__FILE__,__LINE__)
void *_chunkdup (const void *, size_t, const char *, const char *, int);
#define chunkdup(ptr,siz,label) _chunkdup((ptr),(siz),(label),__FILE__,__LINE__)
char *_chunk_strncat (const char *, const char *, size_t, const char *, const char *, int);
#define chunk_strncat(str,new,n,label) _chunk_strncat((str),(new),(n),(label),__FILE__,__LINE__)
char *_chunk_sprintf (const char *, const char *, int, const char *, ...);
#define chunk_sprintf(label,fmt,...) _chunk_sprintf((label),__FILE__,__LINE__,(fmt),__VA_ARGS__)
#else /* CHUNK_DIAG */
void *chunk (size_t, const char *);
void *rechunk (void *, size_t, const char *);
void *chunkdup (void *, size_t, const char *);
char *chunk_strncat (const char *, const char *, size_t, const char *);
char *chunk_sprintf (const char *, char *, ...);
#endif /* CHUNK_DIAG */
void unchunk (void *);
void unchunk_group (const char *);
void chunk_check (FILE *, const char *);
size_t chunksize (void *);
#endif /* _CLICON_CHUNK_H_ */

80
lib/clicon/clicon_err.h Normal file
View file

@ -0,0 +1,80 @@
/*
*
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/>.
*
* Errors may be syslogged using LOG_ERR, and printed to stderr, as controlled by
* clicon_log_init
* global error variables are set:
* clicon_errno, clicon_suberrno, clicon_err_reason.
*/
#ifndef _CLICON_ERR_H_
#define _CLICON_ERR_H_
/*
* Constants
*/
#define ERR_STRLEN 256
/*
* Types
* Add error here, but must also add an entry in EV variable.
*/
enum clicon_err{
/* 0 means error not set) */
OE_DB = 1, /* database registries */
OE_DEMON, /* demons: pidfiles, etc */
OE_EVENTS, /* events, filedescriptors, timeouts */
OE_CFG, /* config commit / quagga */
OE_PROTO, /* config/client communication */
OE_REGEX, /* Regexp error */
OE_UNIX, /* unix/linux syscall error */
OE_SYSLOG, /* syslog error */
OE_ROUTING, /* routing daemon error (eg quagga) */
OE_XML, /* xml parsing etc */
OE_PLUGIN, /* plugin loading, etc */
OE_YANG , /* Yang error */
OE_FATAL, /* Fatal error */
OE_UNDEF,
};
/*
* Variables
* XXX: should not be global
*/
extern int clicon_errno; /* CLICON errors (see clicon_err) */
extern int clicon_suberrno; /* Eg orig errno */
extern char clicon_err_reason[ERR_STRLEN];
/*
* Macros
*/
#define clicon_err(e,s,_fmt, args...) clicon_err_fn(__FUNCTION__, __LINE__, (e), (s), _fmt , ##args)
/*
* Prototypes
*/
int clicon_err_reset(void);
int clicon_err_fn(const char *fn, const int line, int level, int err, char *format, ...);
char *clicon_strerror(int err);
void *clicon_err_save(void);
int clicon_err_restore(void *handle);
#endif /* _CLICON_ERR_H_ */

46
lib/clicon/clicon_event.h Normal file
View file

@ -0,0 +1,46 @@
/*
*
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/>.
*
* Event handling and loop
*/
#ifndef _CLICON_EVENT_H_
#define _CLICON_EVENT_H_
/*
* Prototypes
*/
int clicon_exit_set(void);
int clicon_exit_get(void);
int event_reg_fd(int fd, int (*fn)(int, void*), void *arg, char *str);
int event_unreg_fd(int s, int (*fn)(int, void*));
int event_reg_timeout(struct timeval t, int (*fn)(int, void*),
void *arg, char *str);
int event_unreg_timeout(int (*fn)(int, void*), void *arg);
int event_loop(void);
#endif /* _CLICON_EVENT_H_ */

36
lib/clicon/clicon_file.h Normal file
View file

@ -0,0 +1,36 @@
/*
*
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/>.
*/
#ifndef _CLICON_FILE_H_
#define _CLICON_FILE_H_
char **clicon_realpath(const char *cwd, char *path, const char *label);
int clicon_file_dirent(const char *dir, struct dirent **ent,
const char *regexp, mode_t type, const char *label);
char *clicon_tmpfile(const char *label);
int file_cp(char *src, char *target);
#endif /* _CLICON_FILE_H_ */

View file

@ -0,0 +1,61 @@
/*
*
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/>.
*/
#ifndef _CLICON_HANDLE_H_
#define _CLICON_HANDLE_H_
/*
* Types
*/
/* Common handle used in most clicon calls that you get from clicon_init().
Note that its contents is different dependending on if invoked from a
cli/backend/netconf or other plugin. But this is hidden under-the-hood.
*/
#if 1 /* SANITY CHECK */
typedef struct {float a;} *clicon_handle;
#else
typedef void *clicon_handle;
#endif
/*
* Prototypes
*/
/* Basic CLICON init functions returning a handle for API access. */
clicon_handle clicon_handle_init(void);
/* Internal call to allocate a CLICON handle. */
clicon_handle clicon_handle_init0(int size);
/* Deallocate handle */
int clicon_handle_exit(clicon_handle h);
/* Check struct magic number for sanity checks */
int clicon_handle_check(clicon_handle h);
/* Return clicon options (hash-array) given a handle.*/
clicon_hash_t *clicon_options(clicon_handle h);
/* Return internal clicon data (hash-array) given a handle.*/
clicon_hash_t *clicon_data(clicon_handle h);
#endif /* _CLICON_HANDLE_H_ */

70
lib/clicon/clicon_hash.h Normal file
View file

@ -0,0 +1,70 @@
/*
*
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/>.
*/
#ifndef _CLICON_HASH_H_
#define _CLICON_HASH_H_
struct clicon_hash {
qelem_t h_qelem;
char *h_key;
size_t h_vlen;
void *h_val;
};
typedef struct clicon_hash *clicon_hash_t;
clicon_hash_t *hash_init (void);
void hash_free (clicon_hash_t *);
clicon_hash_t hash_lookup (clicon_hash_t *head, const char *key);
void *hash_value (clicon_hash_t *head, const char *key, size_t *vlen);
clicon_hash_t hash_add (clicon_hash_t *head, const char *key, void *val, size_t vlen);
int hash_del (clicon_hash_t *head, const char *key);
void hash_dump(clicon_hash_t *head, FILE *f);
char **hash_keys(clicon_hash_t *hash, size_t *nkeys);
/*
* Macros to iterate over hash contents.
* XXX A bit crude. Just as easy for app to loop through the keys itself.
*
* Example:
* char *k;
* clicon_hash_t *h = hash_init();
*
* hash_add(h, "colour", "red", 6);
* hash_add(h, "name", "rudolf" 7);
* hash_add(h, "species", "reindeer" 9);
*
* hash_each(h, k) {
* printf ("%s = %s\n", k, (char *)hash_value(h, k, NULL));
* } hash_each_end();
*/
#define hash_each(__hash__, __key__) \
{ \
int __i__; \
size_t __n__; \
char **__k__ = hash_keys((__hash__),&__n__); \
if (__k__) { \
for(__i__ = 0; __i__ < __n__ && ((__key__) = __k__[__i__]); __i__++)
#define hash_each_end(__hash__) if (__k__) free(__k__); } }
#endif /* _CLICON_HASH_H_ */

57
lib/clicon/clicon_log.h Normal file
View file

@ -0,0 +1,57 @@
/*
*
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/>.
*
* Regular logging and debugging. Syslog using levels.
*/
#ifndef _CLICON_LOG_H_
#define _CLICON_LOG_H_
/*
* Constants
*/
#define CLICON_LOG_SYSLOG 1 /* print logs on syslog */
#define CLICON_LOG_STDERR 2 /* print logs on stderr */
#define CLICON_LOG_STDOUT 4 /* print logs on stdout */
/*
* Types
*/
typedef int (clicon_log_notify_t)(int level, char *msg, void *arg);
/*
* Variables
*/
extern int debug;
/*
* Prototypes
*/
int clicon_log_init(char *ident, int upto, int flags);
int clicon_log_str(int level, char *msg);
int clicon_log(int level, char *format, ...);
clicon_log_notify_t *clicon_log_register_callback(clicon_log_notify_t *cb, void *arg);
int clicon_debug_init(int dbglevel, FILE *f);
int clicon_debug(int dbglevel, char *format, ...);
char *mon2name(int md);
#endif /* _CLICON_LOG_H_ */

114
lib/clicon/clicon_options.h Normal file
View file

@ -0,0 +1,114 @@
/*
*
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/>.
*
* Configuration file and Options.
*/
#ifndef _CLICON_OPTIONS_H_
#define _CLICON_OPTIONS_H_
/*
* Constants
*/
/* default group membership to access config unix socket */
#define CLICON_SOCK_GROUP "clicon"
/* Default name of master plugin */
#define CLICON_MASTER_PLUGIN "master"
/*
* Types
*/
/*
* enum gensyntx
* Controls how keywords a generated in CLI syntax / prints from obhect model
* Example syntax a.b[] $!x $y:
* NONE: a b <x> <y>;
* VARS: a b <x> y <y>;
* ALL: a b x <x> y <y>;
*/
enum genmodel_type{
GT_ERR =-1, /* Error */
GT_NONE=0, /* No extra keywords */
GT_VARS, /* Keywords on non-index variables */
GT_ALL, /* Keywords on all variables */
};
/*
* Prototypes
*/
/* Initialize options: set defaults, read config-file, etc */
int clicon_options_main(clicon_handle h);
void clicon_option_dump(clicon_handle h, int dblevel);
int clicon_option_exists(clicon_handle h, const char *name);
/* Get a single option via handle */
char *clicon_option_str(clicon_handle h, const char *name);
int clicon_option_int(clicon_handle h, const char *name);
/* Set a single option via handle */
int clicon_option_str_set(clicon_handle h, const char *name, char *val);
int clicon_option_int_set(clicon_handle h, const char *name, int val);
/* Delete a single option via handle */
int clicon_option_del(clicon_handle h, const char *name);
char *clicon_configfile(clicon_handle h);
char *clicon_yang_dir(clicon_handle h);
char *clicon_yang_module_main(clicon_handle h);
char *clicon_yang_module_revision(clicon_handle h);
char *clicon_running_db(clicon_handle h);
char *clicon_candidate_db(clicon_handle h);
char *clicon_backend_dir(clicon_handle h);
char *clicon_cli_dir(clicon_handle h);
char *clicon_clispec_dir(clicon_handle h);
char *clicon_netconf_dir(clicon_handle h);
char *clicon_archive_dir(clicon_handle h);
char *clicon_startup_config(clicon_handle h);
int clicon_sock_family(clicon_handle h);
char *clicon_sock(clicon_handle h);
int clicon_sock_port(clicon_handle h);
char *clicon_backend_pidfile(clicon_handle h);
char *clicon_sock_group(clicon_handle h);
char *clicon_master_plugin(clicon_handle h);
char *clicon_cli_mode(clicon_handle h);
int clicon_cli_genmodel(clicon_handle h);
int clicon_cli_varonly(clicon_handle h);
int clicon_cli_varonly_set(clicon_handle h, int val);
int clicon_cli_genmodel_completion(clicon_handle h);
char *clicon_quiet_mode(clicon_handle h);
enum genmodel_type clicon_cli_genmodel_type(clicon_handle h);
int clicon_autocommit(clicon_handle h);
int clicon_autocommit_set(clicon_handle h, int val);
int clicon_commit_order(clicon_handle h);
yang_spec * clicon_dbspec_yang(clicon_handle h);
int clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys);
char *clicon_dbspec_name(clicon_handle h);
int clicon_dbspec_name_set(clicon_handle h, char *name);
#endif /* _CLICON_OPTIONS_H_ */

View file

@ -0,0 +1,67 @@
/*
*
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/>.
*/
/*
* Internal prototypes, not accessed by plugin client code
*/
#ifndef _CLICON_PLUGIN_H_
#define _CLICON_PLUGIN_H_
/*
* Types
*/
/* The dynamicically loadable plugin object handle */
typedef void *plghndl_t;
/* Find plugin by name callback. XXX Should be clicon internal */
typedef void *(find_plugin_t)(clicon_handle, char *);
/*
* Prototypes
*/
/* Common plugin function names, function types and signatures.
* This set of plugins is extended in
* Cli see cli_plugin.c
* Backend see config_plugin.c
*/
/*! Called when plugin loaded. Only mandadory callback. All others optional
* @see plginit_t
*/
#define PLUGIN_INIT "plugin_init"
typedef int (plginit_t)(clicon_handle); /* Plugin Init */
/* Called when backend started with cmd-line arguments from daemon call.
* @see plgstart_t
*/
#define PLUGIN_START "plugin_start"
typedef int (plgstart_t)(clicon_handle, int, char **); /* Plugin start */
/* Called just before plugin unloaded.
* @see plgexit_t
*/
#define PLUGIN_EXIT "plugin_exit"
typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
/* Find a function in global namespace or a plugin. XXX clicon internal */
void *clicon_find_func(clicon_handle h, char *plugin, char *func);
#endif /* _CLICON_PLUGIN_H_ */

33
lib/clicon/clicon_proc.h Normal file
View file

@ -0,0 +1,33 @@
/*
*
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/>.
*/
#ifndef _CLICON_PROC_H_
#define _CLICON_PROC_H_
/*
* Prototypes
*/
int clicon_proc_run (char *, void (outcb)(char *), int doerr);
int clicon_proc_daemon (char *);
int group_name2gid(char *name, gid_t *gid);
#endif /* _CLICON_PROC_H_ */

183
lib/clicon/clicon_proto.h Normal file
View file

@ -0,0 +1,183 @@
/*
*
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/>.
*
* Protocol to communicate with CLICON config daemon
*/
#ifndef _CLICON_PROTO_H_
#define _CLICON_PROTO_H_
/*
* Types
*/
enum format_enum{
MSG_NOTIFY_TXT, /* means filter works on strings */
MSG_NOTIFY_XML, /* means filter works on xml */
};
/* See also map_type2str in clicon_proto.c */
enum clicon_msg_type{
CLICON_MSG_COMMIT = 1, /* Commit a configuration db->running_db
current state, set running_db. Body is:
1. uint32: (1)snapshot while doing commit, (0) dont
2. uint32: (1)save to startup-config, (0) dont
3. string: name of 'from' database (eg candidate)
4. string: name of 'to' database (eg current)
*/
CLICON_MSG_VALIDATE, /* Validate settings in a database. Body is:
1. string: name of database
*/
CLICON_MSG_CHANGE, /* Change a (single) database entry:
1. uint32: operation: OP_MERGE/OP_REPLACE/OP_REMOVE
2. uint32: length of value string
3. string: name of database to change (eg current)
4. string: key
5. string: value
*/
CLICON_MSG_XMLPUT, /* Send database entries as XML to backend daemon
1. uint32: operation: LV_SET/LV_DELETE
2. string: name of database to change (eg current)
3. string: XML data
*/
CLICON_MSG_SAVE, /* Save config state from db to a file in backend. Body is:
1. uint32: make snapshot (1), dont(0)
2. string: name of database to save from (eg running)
3. string: filename to write. If snapshot=1, then this
is empty.
*/
CLICON_MSG_LOAD, /* Load config state from file in backend to db via XML. Body is:
1. uint32: whether to replace/initdb before load (1) or
merge (0).
2. string: name of database to load into (eg running)
3. string: filename to load from
*/
CLICON_MSG_COPY, /* Copy from file to file in backend. Body is:
1. string: filename to copy from
2. string: filename to copy to
*/
CLICON_MSG_RM , /* Delete file. Body is:
1. string: filename to delete
*/
CLICON_MSG_INITDB , /* (Re-)Initialize a database. Body is:
1. string: filename of db to initialize
*/
CLICON_MSG_LOCK , /* Lock a database. Body is
1. name of db
The reply will be OK, or ERROR. If error is
lock-denied, the session-id of the locking
entity is returned (cf netconf)
*/
CLICON_MSG_UNLOCK , /* Unlock a database. Body is:
1. name of db *
*/
CLICON_MSG_KILL, /* Kill (other) session:
1. session-id
*/
CLICON_MSG_DEBUG, /* Debug
1. session-id
*/
CLICON_MSG_CALL , /* Backend plugin call request. Body is:
1. struct clicon_msg_call_req *
*/
CLICON_MSG_SUBSCRIPTION, /* Create a new notification subscription.
Body is:
1. int: status off/on
1. int: format (enum format_enum)
2. string: name of notify stream
3. string: filter, if format=xml: xpath, if text: fnmatch */
CLICON_MSG_OK, /* server->client reply */
CLICON_MSG_NOTIFY, /* Notification. Body is:
1. int: loglevel
2. event: log message. */
CLICON_MSG_ERR /* server->client reply.
Body is:
1. uint32: man error category
2. uint32: sub-error
3. string: reason
*/
};
/* Protocol message header */
struct clicon_msg {
uint16_t op_len; /* length of message. */
uint16_t op_type; /* message type, see enum clicon_msg_type */
char op_body[0]; /* rest of message, actual data */
};
/* Generic clicon message. Either generic/internal message
or application-specific backend plugin downcall request */
struct clicon_msg_call_req {
uint16_t cr_len; /* Length of total request */
uint16_t cr_op; /* Generic application-defined operation */
char *cr_plugin; /* Name of backend plugin, NULL -> internal
functions */
char *cr_func; /* Function name in plugin (or internal) */
uint16_t cr_arglen; /* App specific argument length */
char *cr_arg; /* App specific argument */
char cr_data[0]; /* Allocated data containng the above */
};
/*
* Prototypes
*/
#ifndef LIBCLICON_API
int clicon_connect_unix(char *sockpath);
int clicon_rpc_connect_unix(struct clicon_msg *msg,
char *sockpath,
char **data,
uint16_t *datalen,
int *sock0,
const char *label);
int clicon_rpc_connect_inet(struct clicon_msg *msg,
char *dst,
uint16_t port,
char **data,
uint16_t *datalen,
int *sock0,
const char *label);
int clicon_rpc(int s, struct clicon_msg *msg, char **data, uint16_t *datalen,
const char *label);
#endif
int clicon_msg_send(int s, struct clicon_msg *msg);
int clicon_msg_rcv(int s, struct clicon_msg **msg,
int *eof, const char *label);
int send_msg_notify(int s, int level, char *event);
int send_msg_reply(int s, uint16_t type, char *data, uint16_t datalen);
int send_msg_ok(int s);
int send_msg_err(int s, int err, int suberr, char *format, ...);
#endif /* _CLICON_PROTO_H_ */

View file

@ -0,0 +1,57 @@
/*
*
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/>.
*
* Client-side functions for clicon_proto protocol
* Historically this code was part of the clicon_cli application. But
* it should (is?) be general enough to be used by other applications.
*/
#ifndef _CLICON_PROTO_CLIENT_H_
#define _CLICON_PROTO_CLIENT_H_
int clicon_rpc_commit(clicon_handle h, char *running_db, char *db,
int snapshot, int startup);
int clicon_rpc_validate(clicon_handle h, char *db);
int clicon_rpc_change(clicon_handle h, char *db,
enum operation_type op, char *key, char *val);
int clicon_rpc_xmlput(clicon_handle h, char *db, enum operation_type op, char *xml);
int clicon_rpc_dbitems(clicon_handle h, char *db, char *rx,
char *attr, char *val,
cvec ***cvv, size_t *cvvlen);
int clicon_rpc_save(clicon_handle h, char *dbname, int snapshot, char *filename);
int clicon_rpc_load(clicon_handle h, int replace, char *db, char *filename);
int clicon_rpc_copy(clicon_handle h, char *filename1, char *filename2);
int clicon_rpc_rm(clicon_handle h, char *filename);
int clicon_rpc_initdb(clicon_handle h, char *filename);
int clicon_rpc_lock(clicon_handle h, char *dbname);
int clicon_rpc_unlock(clicon_handle h, char *dbname);
int clicon_rpc_kill(clicon_handle h, int session_id);
int clicon_rpc_debug(clicon_handle h, int level);
int clicon_rpc_call(clicon_handle h, uint16_t op, char *plugin, char *func,
void *param, uint16_t paramlen,
char **ret, uint16_t *retlen,
const void *label);
int clicon_rpc_subscription(clicon_handle h, int status, char *stream,
enum format_enum format, char *filter, int *s);
#endif /* _CLICON_PROTO_CLIENT_H_ */

View file

@ -0,0 +1,189 @@
/*
*
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/>.
*
* Protocol to communicate between clients (eg clicon_cli, clicon_netconf)
* and server (clicon_backend)
*/
#ifndef _CLICON_PROTO_ENCODE_H_
#define _CLICON_PROTO_ENCODE_H_
/*
* Prototypes
*/
struct clicon_msg *
clicon_msg_commit_encode(char *dbsrc, char *dbdst,
uint32_t snapshot, uint32_t startup,
const char *label);
int
clicon_msg_commit_decode(struct clicon_msg *msg,
char **dbsrc, char **dbdst,
uint32_t *snapshot, uint32_t *startup,
const char *label);
struct clicon_msg *
clicon_msg_validate_encode(char *db,
const char *label);
int
clicon_msg_validate_decode(struct clicon_msg *msg, char **db,
const char *label);
struct clicon_msg *
clicon_msg_change_encode(char *db, uint32_t op, char *key,
char *lvec, uint32_t lvec_len,
const char *label);
int
clicon_msg_change_decode(struct clicon_msg *msg,
char **db, uint32_t *op, char **key,
char **lvec, uint32_t *lvec_len,
const char *label);
struct clicon_msg *
clicon_msg_xmlput_encode(char *db,
uint32_t op,
char *xml,
const char *label);
int
clicon_msg_xmlput_decode(struct clicon_msg *msg,
char **db,
uint32_t *op,
char **filename,
const char *label);
struct clicon_msg *
clicon_msg_dbitems_get_reply_encode(cvec **cvecv,
int cveclen,
const char *label);
int
clicon_msg_dbitems_get_reply_decode(char *data,
uint16_t datalen,
cvec ***cvecv,
size_t *cveclen,
const char *label);
struct clicon_msg *
clicon_msg_save_encode(char *db, uint32_t snapshot, char *filename,
const char *label);
int
clicon_msg_save_decode(struct clicon_msg *msg,
char **db, uint32_t *snapshot, char **filename,
const char *label);
struct clicon_msg *
clicon_msg_load_encode(int replace, char *db, char *filename,
const char *label);
int
clicon_msg_load_decode(struct clicon_msg *msg,
int *replace, char **db, char **filename,
const char *label);
struct clicon_msg *
clicon_msg_initdb_encode(char *filename_src, const char *label);
int
clicon_msg_initdb_decode(struct clicon_msg *msg, char **filename_src,
const char *label);
struct clicon_msg *
clicon_msg_rm_encode(char *filename_src, const char *label);
int
clicon_msg_rm_decode(struct clicon_msg *msg, char **filename_src,
const char *label);
struct clicon_msg *
clicon_msg_copy_encode(char *filename_src, char *filename_dst,
const char *label);
int
clicon_msg_copy_decode(struct clicon_msg *msg,
char **filename_src, char **filename_dst,
const char *label);
struct clicon_msg *
clicon_msg_lock_encode(char *db, const char *label);
int
clicon_msg_lock_decode(struct clicon_msg *msg, char **db, const char *label);
struct clicon_msg *
clicon_msg_unlock_encode(char *db, const char *label);
int
clicon_msg_unlock_decode(struct clicon_msg *msg, char **db, const char *label);
struct clicon_msg *
clicon_msg_kill_encode(uint32_t session_id, const char *label);
int
clicon_msg_kill_decode(struct clicon_msg *msg, uint32_t *session_id,
const char *label);
struct clicon_msg *
clicon_msg_debug_encode(uint32_t level, const char *label);
int
clicon_msg_debug_decode(struct clicon_msg *msg, uint32_t *level,
const char *label);
struct clicon_msg *
clicon_msg_call_encode(uint16_t op, char *plugin, char *func,
uint16_t arglen, void *arg,
const char *label);
int
clicon_msg_call_decode(struct clicon_msg *msg,
struct clicon_msg_call_req **req,
const char *label);
struct clicon_msg *
clicon_msg_subscription_encode(int status,
char *stream,
enum format_enum format,
char *filter,
const char *label);
int clicon_msg_subscription_decode(struct clicon_msg *msg,
int *status,
char **stream,
enum format_enum *format,
char **filter,
const char *label);
struct clicon_msg *
clicon_msg_notify_encode(int level, char *event, const char *label);
int
clicon_msg_notify_decode(struct clicon_msg *msg, int *level,
char **event, const char *label);
struct clicon_msg *clicon_msg_err_encode(uint32_t err, uint32_t suberr,
char *reason, const char *label);
int clicon_msg_err_decode(struct clicon_msg *msg, uint32_t *err, uint32_t *suberr,
char **reason, const char *label);
#endif /* _CLICON_PROTO_ENCODE_H_ */

58
lib/clicon/clicon_qdb.h Normal file
View file

@ -0,0 +1,58 @@
/*
*
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/>.
*/
#ifndef _CLICON_QDB_H_
#define _CLICON_QDB_H_
/*
* Low level API
*/
struct db_pair {
char *dp_key; /* database key */
char *dp_matched; /* Matched component of key */
char *dp_val; /* pointer to vector of lvalues */
int dp_vlen; /* length of vector of lvalues */
};
/*
* Prototypes
*/
int db_init(char *file);
int db_set(char *file, char *key, void *data, size_t datalen);
int db_get(char *file, char *key, void *data, size_t *datalen);
int db_get_alloc(char *file, char *key, void **data, size_t *datalen);
int db_del(char *file, char *key);
int db_exists(char *file, char *key);
int db_regexp(char *file, char *regexp, const char *label,
struct db_pair **pairs, int noval);
char *db_sanitize(char *rx, const char *label);
#endif /* _CLICON_QDB_H_ */

89
lib/clicon/clicon_queue.h Normal file
View file

@ -0,0 +1,89 @@
/*
*
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/>.
*
* Copyright (C) 2002 Benny Holmgren, All rights reserved
*/
#ifndef _CLICON_QUEUE_H_
#define _CLICON_QUEUE_H_
/*
* Circular queue structure for use as first entry in a parent structure.
*/
typedef struct _qelem_t {
struct _qelem_t *q_next;
struct _qelem_t *q_prev;
} qelem_t;
/*
* Append element 'elem' to queue.
*/
#define ADDQ(elem, pred) { \
register qelem_t *Xe = (qelem_t *) (elem); \
register qelem_t *Xp = (qelem_t *) (pred); \
if (pred) { \
Xe->q_next = Xp; \
Xe->q_prev = Xp->q_prev; \
Xp->q_prev->q_next = Xe; \
Xp->q_prev = Xe; \
} else { \
Xe->q_next = Xe->q_prev = Xe; \
pred = elem; \
} \
}
/*
* Insert element 'elem' in queue after 'pred'
*/
#define INSQ(elem, pred) { \
register qelem_t *Xe = (qelem_t *) (elem); \
register qelem_t *Xp = (qelem_t *) (pred); \
if (pred) { \
Xe->q_next = Xp; \
Xe->q_prev = Xp->q_prev; \
Xp->q_prev->q_next = Xe; \
Xp->q_prev = Xe; \
} else { \
Xe->q_next = Xe->q_prev = Xe; \
} \
pred = elem; \
}
/*
* Remove element 'elem' from queue. 'head' is the pointer to the queue and
* is of 'type'.
*/
#define DELQ(elem, head, type) { \
register qelem_t *Xe = (qelem_t *) elem; \
if (Xe->q_next == Xe) \
head = NULL; \
(Xe->q_prev->q_next = Xe->q_next)->q_prev = Xe->q_prev; \
if (elem == head) \
head = (type)Xe->q_next; \
}
/*
* Get next entry in list
*/
#define NEXTQ(type, elem) ((type)((elem)?((qelem_t *)(elem))->q_next:NULL))
#endif /* _CLICON_QUEUE_H_ */

30
lib/clicon/clicon_sha1.h Normal file
View file

@ -0,0 +1,30 @@
/*
*
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/>.
*/
#ifndef _CLICON_SHA1_H_
#define _CLICON_SHA1_H_
/*
* Function Prototypes
*/
char *clicon_sha1hex(const char *str);
#endif /* _CLICON_SHA1_H_ */

42
lib/clicon/clicon_sig.h Normal file
View file

@ -0,0 +1,42 @@
/*
*
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/>.
*/
#ifndef _CLICON_SIG_H_
#define _CLICON_SIG_H_
/*
* Types
*/
typedef void (*sigfn_t)(int);
/*
* Prototypes
*/
int set_signal(int signo, void (*handler)(int), void (**oldhandler)(int));
void clicon_signal_block(int);
void clicon_signal_unblock(int);
int pidfile_get(char *pidfile, pid_t *pid0);
int pidfile_write(char *pidfile);
int pidfile_zapold(pid_t pid);
#endif /* _CLICON_SIG_H_ */

View file

@ -0,0 +1,56 @@
/*
*
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/>.
*/
#ifndef _CLICON_STRING_H_
#define _CLICON_STRING_H_
/*! A malloc version that aligns on 4 bytes. To avoid warning from valgrind */
#define align4(s) (((s)/4)*4 + 4)
/*! A strdup version that aligns on 4 bytes. To avoid warning from valgrind */
static inline char * strdup4(char *str)
{
char *dup;
int len;
len = align4(strlen(str)+1);
if ((dup = malloc(len)) == NULL)
return NULL;
strncpy(dup, str, len);
return dup;
}
/*
* Prototypes
*/
char **clicon_sepsplit (char *string, char *delim, int *nvec, const char *label);
char **clicon_strsplit (char *string, char *delim, int *nvec, const char *label);
char *clicon_strjoin (int argc, char **argv, char *delim, const char *label);
char *clicon_strtrim(char *str, const char *label);
int clicon_sep(char *s, const char sep[2], const char *label, char**a0, char **b0);
#ifndef HAVE_STRNDUP
char *clicon_strndup (const char *, size_t);
#endif /* ! HAVE_STRNDUP */
int clicon_strmatch(const char *str, const char *regexp, char **match);
char *clicon_strsub(char *str, char *from, char *to);
#endif /* _CLICON_STRING_H_ */

108
lib/clicon/clicon_xml.h Normal file
View file

@ -0,0 +1,108 @@
/*
*
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/>.
* XML support functions.
*/
#ifndef _CLICON_XML_H
#define _CLICON_XML_H
/*
* Types
*/
/* Netconf operation type */
enum operation_type{ /* edit-config */
OP_MERGE, /* merge config-data */
OP_REPLACE,/* replace or create config-data */
OP_CREATE, /* create config data, error if exist */
OP_DELETE, /* delete config data, error if it does not exist */
OP_REMOVE, /* delete config data */
OP_NONE
};
enum cxobj_type {CX_ERROR=-1,
CX_ELMNT,
CX_ATTR,
CX_BODY};
#define CX_ANY CX_ERROR /* catch all and error is same */
typedef struct xml cxobj; /* struct defined in clicon_xml.c */
/*! Callback function type for xml_apply */
typedef int (xml_applyfn_t)(cxobj *yn, void *arg);
/*
* xml_flag() flags:
*/
#define XML_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
/*
* Prototypes
*/
char *xml_name(cxobj *xn);
int xml_name_set(cxobj *xn, char *name);
char *xml_namespace(cxobj *xn);
int xml_namespace_set(cxobj *xn, char *name);
cxobj *xml_parent(cxobj *xn);
int xml_parent_set(cxobj *xn, cxobj *parent);
uint16_t xml_flag(cxobj *xn, uint16_t flag);
int xml_flag_set(cxobj *xn, uint16_t flag);
int xml_flag_reset(cxobj *xn, uint16_t flag);
char *xml_value(cxobj *xn);
int xml_value_set(cxobj *xn, char *val);
char *xml_value_append(cxobj *xn, char *val);
enum cxobj_type xml_type(cxobj *xn);
int xml_type_set(cxobj *xn, enum cxobj_type type);
int xml_index(cxobj *xn);
int xml_index_set(cxobj *xn, int index);
int xml_child_nr(cxobj *xn);
cxobj *xml_child_i(cxobj *xn, int i);
cxobj *xml_child_i_set(cxobj *xt, int i, cxobj *xc);
cxobj *xml_child_each(cxobj *xparent, cxobj *xprev, enum cxobj_type type);
int xml_childvec_set(cxobj *x, int len);
cxobj *xml_new(char *name, cxobj *xn_parent);
cxobj *xml_new_spec(char *name, cxobj *xn_parent, void *spec);
void *xml_spec(cxobj *x);
cxobj *xml_find(cxobj *xn_parent, char *name);
char *xml_body(cxobj *xn);
char *xml_find_value(cxobj *xn_parent, char *name);
char *xml_find_body(cxobj *xn, char *name);
int xml_free(cxobj *xn);
int xml_prune(cxobj *xp, cxobj *xc, int freeit);
int clicon_xml2file(FILE *f, cxobj *xn, int level, int prettyprint);
int clicon_xml2cbuf(cbuf *xf, cxobj *xn, int level, int prettyprint);
int clicon_xml_parse_file(int fd, cxobj **xml_top, char *endtag);
int clicon_xml_parse_string(char **str, cxobj **xml_top);
int xml_copy(cxobj *x0, cxobj *x1);
cxobj *xml_dup(cxobj *x0);
int xml_addsub(cxobj *xp, cxobj *xc);
cxobj *xml_insert(cxobj *xt, char *tag);
int cxvec_dup(cxobj **vec0, size_t len0, cxobj ***vec1, size_t *len1);
int cxvec_append(cxobj *x, cxobj ***vec, size_t *len);
int xml_apply(cxobj *xn, enum cxobj_type type, xml_applyfn_t fn, void *arg);
#endif /* _CLICON_XML_H */

View file

@ -0,0 +1,42 @@
/*
*
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/>.
* XML support functions.
*/
#ifndef _CLICON_XML_DB_H
#define _CLICON_XML_DB_H
/*
* Prototypes
*/
int yang2xmlkeyfmt(yang_stmt *ys, char **xkfmt);
int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk);
int xmlkeyfmt2key2(char *xkfmt, cvec *cvv, char **xk);
int xmlkey2xml(char *xkey, yang_spec *yspec, char **xml);
int xmldb_get(char *dbname, char *xpath,
yang_spec *yspec, cxobj **xtop);
int xmldb_get_xpath(char *dbname, char *xpath, yang_spec *yspec,
cxobj **xtop, cxobj ***xvec, int *xlen);
int xmldb_put( char *dbname, cxobj *xt,
yang_spec *yspec, enum operation_type op);
int xmldb_put_xkey(char *dbname, char *xkey, char *val, yang_spec *yspec,
enum operation_type op);
#endif /* _CLICON_XML_DB_H */

View file

@ -0,0 +1,53 @@
/*
*
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/>.
*
* XML code
*/
#ifndef _CLICON_XML_MAP_H_
#define _CLICON_XML_MAP_H_
/*
* lvmap_xml op codes
*/
enum {
LVXML, /* a.b{x=1} -> <a><b>1 */
LVXML_VAL, /* a.b{x=1} -> <a><b><x>1 */
LVXML_VECVAL, /* key: a.b.0{x=1} -> <a><b><x>1</x></b></a> och */
LVXML_VECVAL2, /* key: a.b.0{x=1} -> <a><x>1</x></a> och */
};
/*
* Prototypes
*/
int xml2txt(FILE *f, cxobj *x, int level);
int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt, const char *label);
int xml2json(FILE *f, cxobj *x, int level);
int xml_yang_validate(cxobj *xt, yang_stmt *ys) ;
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
int cvec2xml_1(cvec *cvv, char *toptag, cxobj **xt0);
int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,
cxobj ***first, size_t *firstlen,
cxobj ***second, size_t *secondlen,
cxobj ***changed1, cxobj ***changed2, size_t *changedlen);
#endif /* _CLICON_XML_MAP_H_ */

33
lib/clicon/clicon_xsl.h Normal file
View file

@ -0,0 +1,33 @@
/*
*
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/>.
* XML XPATH and XSLT functions.
*/
#ifndef _CLICON_XSL_H
#define _CLICON_XSL_H
/*
* Prototypes
*/
cxobj *xpath_first(cxobj *xn_top, char *xpath);
cxobj *xpath_each(cxobj *xn_top, char *xpath, cxobj *prev);
cxobj **xpath_vec(cxobj *xn_top, char *xpath, int *xv_len);
#endif /* _CLICON_XSL_H */

206
lib/clicon/clicon_yang.h Normal file
View file

@ -0,0 +1,206 @@
/*
*
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/>.
*/
#ifndef _CLICON_YANG_H_
#define _CLICON_YANG_H_
/*
* Actually cligen variable stuff XXX
*/
#define V_UNIQUE 0x01 /* Variable flag */
#define V_UNSET 0x08 /* Variable is unset, ie no default */
/*
* Types
*/
/*! YANG keywords from RFC6020.
* See also keywords generated by yacc/bison in clicon_yang_parse.tab.h, but they start with K_
* instead of Y_
* Wanted to unify these (K_ and Y_) but gave up for several reasons:
* - Dont want to expose a generated yacc file to the API
* - Cant use the symbols in this file because yacc needs token definitions
*/
enum rfc_6020{
Y_ANYXML,
Y_ARGUMENT,
Y_AUGMENT,
Y_BASE,
Y_BELONGS_TO,
Y_BIT,
Y_CASE,
Y_CHOICE,
Y_CONFIG,
Y_CONTACT,
Y_CONTAINER,
Y_DEFAULT,
Y_DESCRIPTION,
Y_DEVIATE,
Y_DEVIATION,
Y_ENUM,
Y_ERROR_APP_TAG,
Y_ERROR_MESSAGE,
Y_EXTENSION,
Y_FEATURE,
Y_FRACTION_DIGITS,
Y_GROUPING,
Y_IDENTITY,
Y_IF_FEATURE,
Y_IMPORT,
Y_INCLUDE,
Y_INPUT,
Y_KEY,
Y_LEAF,
Y_LEAF_LIST,
Y_LENGTH,
Y_LIST,
Y_MANDATORY,
Y_MAX_ELEMENTS,
Y_MIN_ELEMENTS,
Y_MODULE,
Y_MUST,
Y_NAMESPACE,
Y_NOTIFICATION,
Y_ORDERED_BY,
Y_ORGANIZATION,
Y_OUTPUT,
Y_PATH,
Y_PATTERN,
Y_POSITION,
Y_PREFIX,
Y_PRESENCE,
Y_RANGE,
Y_REFERENCE,
Y_REFINE,
Y_REQUIRE_INSTANCE,
Y_REVISION,
Y_REVISION_DATE,
Y_RPC,
Y_STATUS,
Y_SUBMODULE,
Y_TYPE,
Y_TYPEDEF,
Y_UNIQUE,
Y_UNITS,
Y_USES,
Y_VALUE,
Y_WHEN,
Y_YANG_VERSION,
Y_YIN_ELEMENT,
Y_SPEC /* XXX: NOTE NOT YANG STATEMENT, reserved for top level spec */
};
#define YANG_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
typedef struct yang_stmt yang_stmt; /* forward */
/*! Yang type cache. Yang type statements can cache all typedef info here
*/
struct yang_type_cache{
int yc_options;
cg_var *yc_mincv;
cg_var *yc_maxcv;
char *yc_pattern;
uint8_t yc_fraction;
yang_stmt *yc_resolved; /* Resolved type object, can be NULL - note direct ptr */
};
typedef struct yang_type_cache yang_type_cache;
/*! yang statement
*/
struct yang_stmt{
int ys_len; /* Number of children */
struct yang_stmt **ys_stmt; /* Vector of children statement pointers */
struct yang_node *ys_parent; /* Backpointer to parent: yang-stmt or yang-spec */
enum rfc_6020 ys_keyword; /* See clicon_yang_parse.tab.h */
char *ys_argument; /* String / argument depending on keyword */
int ys_flags; /* Flags according to YANG_FLAG_* above */
cg_var *ys_cv; /* cligen variable. The following stmts have cvs::
leaf, leaf-list, mandatory, fraction-digits */
cvec *ys_cvec; /* List of stmt-specific variables
Y_RANGE: range_min, range_max */
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data */
};
/*! top-level yang parse-tree */
struct yang_spec{
int yp_len; /* Number of children */
struct yang_stmt **yp_stmt; /* Vector of children statement pointers */
struct yang_node *yp_parent; /* Backpointer to parent: always NULL. See yang_stmt */
enum rfc_6020 yp_keyword; /* SHOULD BE Y_SPEC */
char *yp_argument; /* XXX String / argument depending on keyword */
int yp_flags; /* Flags according to YANG_FLAG_* above */
};
typedef struct yang_spec yang_spec;
/*! super-class of yang_stmt and yang_spec: it must start exactly as those two classes */
struct yang_node{
int yn_len; /* Number of children */
struct yang_stmt **yn_stmt; /* Vector of children statement pointers */
struct yang_node *yn_parent; /* Backpointer to parent: yang-stmt or yang-spec */
enum rfc_6020 yn_keyword; /* See clicon_yang_parse.tab.h */
char *yn_argument; /* XXX String / argument depending on keyword */
int yn_flags; /* Flags according to YANG_FLAG_* above */
};
typedef struct yang_node yang_node;
typedef int (yang_applyfn_t)(yang_stmt *ys, void *arg);
/*
* Prototypes
*/
yang_spec *yspec_new(void);
yang_stmt *ys_new(enum rfc_6020 keyw);
int ys_free(yang_stmt *ys);
int yspec_free(yang_spec *yspec);
int ys_cp(yang_stmt *new, yang_stmt *old);
yang_stmt *ys_dup(yang_stmt *old);
int yn_insert(yang_node *yn_parent, yang_stmt *ys_child);
yang_stmt *yn_each(yang_node *yn, yang_stmt *ys);
char *yang_key2str(int keyword);
char *ytype_prefix(yang_stmt *ys);
char *ytype_id(yang_stmt *ys);
yang_stmt *ys_module(yang_stmt *ys);
yang_spec *ys_spec(yang_stmt *ys);
yang_stmt *ys_module_import(yang_stmt *ymod, char *prefix);
yang_stmt *yang_find(yang_node *yn, int keyword, char *argument);
yang_stmt *yang_find_syntax(yang_node *yn, char *argument);
yang_stmt *yang_find_topnode(yang_spec *ysp, char *name);
int yang_print(FILE *f, yang_node *yn, int marginal);
int yang_parse(clicon_handle h, const char *yang_dir,
const char *module, const char *revision, yang_spec *ysp);
int yang_apply(yang_node *yn, yang_applyfn_t fn, void *arg);
yang_stmt *dbkey2yang(yang_node *yn, char *dbkey);
yang_node *yang_xpath_abs(yang_node *yn, char *xpath);
yang_node *yang_xpath(yang_node *yn, char *xpath);
cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype);
int ys_parse_sub(yang_stmt *ys);
int yang_mandatory(yang_stmt *ys);
int yang_config(yang_stmt *ys);
int yang_spec_main(clicon_handle h, FILE *f, int printspec);
cvec *yang_arg2cvec(yang_stmt *ys, char *delimi);
int yang_key_match(yang_node *yn, char *name);
#endif /* _CLICON_YANG_H_ */

View file

@ -0,0 +1,68 @@
/*
*
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/>.
*/
#ifndef _CLICON_YANG_TYPE_H_
#define _CLICON_YANG_TYPE_H_
/*
* Constants
*/
/*! Bit-fields used in options argument in yang_type_get()
*/
#define YANG_OPTIONS_LENGTH 0x01
#define YANG_OPTIONS_RANGE 0x02
#define YANG_OPTIONS_PATTERN 0x04
#define YANG_OPTIONS_FRACTION_DIGITS 0x08
/*
* Types
*/
/*
* Prototypes
*/
int yang_type_cache_set(yang_type_cache **ycache,
yang_stmt *resolved, int options, cg_var *mincv,
cg_var *maxcv, char *pattern, uint8_t fraction);
int yang_type_cache_get(yang_type_cache *ycache,
yang_stmt **resolved, int *options, cg_var **mincv,
cg_var **maxcv, char **pattern, uint8_t *fraction);
int yang_type_cache_cp(yang_type_cache **ycnew, yang_type_cache *ycold);
int yang_type_cache_free(yang_type_cache *ycache);
int ys_resolve_type(yang_stmt *ys, void *arg);
int yang2cv_type(char *ytype, enum cv_type *cv_type);
char *cv2yang_type(enum cv_type cv_type);
yang_stmt *yang_find_identity(yang_stmt *ys, char *identity);
int ys_cv_validate(cg_var *cv, yang_stmt *ys, char **reason);
int clicon_type2cv(char *type, char *rtype, enum cv_type *cvtype);
char *ytype_id(yang_stmt *ys);
int yang_type_get(yang_stmt *ys, char **otype, yang_stmt **restype,
int *options, cg_var **mincv, cg_var **maxcv, char **pattern,
uint8_t *fraction_digits);
int yang_type_resolve(yang_stmt *ys, yang_stmt *ytype,
yang_stmt **restype, int *options,
cg_var **mincv, cg_var **maxcv,
char **pattern, uint8_t *fraction);
#endif /* _CLICON_YANG_TYPE_H_ */

173
lib/src/Makefile.in Normal file
View file

@ -0,0 +1,173 @@
#
# Makefile
#
# 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/>.
#
#
prefix = @prefix@
datarootdir = @datarootdir@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
dbdir = @prefix@/db
mandir = @mandir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
sysconfdir = @sysconfdir@
SH_SUFFIX = @SH_SUFFIX@
CLICON_VERSION = @CLICON_VERSION@
CLICON_MAJOR = @CLICON_VERSION_MAJOR@
CLICON_MINOR = @CLICON_VERSION_MINOR@
VPATH = @srcdir@
CC = @CC@
CFLAGS = -fPIC @CFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
YACC = @YACC@
LEX = @LEX@
CPPFLAGS = @CPPFLAGS@
INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clicon -I$(top_srcdir)/include -I$(top_srcdir)
SRC = clicon_sig.c clicon_qdb.c clicon_log.c clicon_err.c clicon_event.c \
clicon_chunk.c clicon_proc.c \
clicon_string.c clicon_handle.c \
clicon_xml.c clicon_xml_map.c clicon_file.c \
clicon_yang.c clicon_yang_type.c \
clicon_hash.c clicon_options.c clicon_plugin.c \
clicon_proto.c clicon_proto_encode.c clicon_proto_client.c \
clicon_xsl.c clicon_sha1.c clicon_xml_db.c
YACCOBJS := lex.clicon_xml_parse.o clicon_xml_parse.tab.o \
lex.clicon_yang_parse.o clicon_yang_parse.tab.o
# Logically, the below 4 should be in YACCOBJS?
# Generated src
GENSRC = build.c
OBJS = $(YACCOBJS) $(SRC:.c=.o)
GENOBJS = $(GENSRC:.c=.o)
# Linker-name: libclicon.so
# so-name: libclicon.so.2
# real-name: libclicon.so.2.0
MYLIB = libclicon$(SH_SUFFIX).$(CLICON_MAJOR).$(CLICON_MINOR)
MYLIBSO = libclicon$(SH_SUFFIX).$(CLICON_MAJOR)
MYLIBLINK = libclicon$(SH_SUFFIX)
all: $(MYLIB) $(MYLIBLINK)
clean:
rm -f $(OBJS) $(MYLIB) $(MYLIBLINK) $(GENOBJS) $(GENSRC) *.core
rm -f clicon_xml_parse.tab.[ch] clicon_xml_parse.yy.[co]
rm -f clicon_yang_parse.tab.[ch] clicon_yang_parse.[co]
rm -f lex.clicon_yang_parse.c
rm -f lex.clicon_xml_parse.c
# disabled when USE_DBSPEC_PT is disabled in clicon_config.h.in
# rm -f clicon_dbspec.tab.[ch] clicon_dbspec.[co]
# rm -f lex.clicon_dbspec.c
#############################################################################
# Implicit rules for lex and yacc.
#
# lex files *.l -> *.yy.c
# yacc files *.y -> *.tab.c and *.tab.h
#
# Lex forces yacc include file *.tab.h to be built.
#############################################################################
%.c : %.y # cancel implicit yacc rule
%.c : %.l # cancel implicit lex rule
# xml parser
lex.clicon_xml_parse.c : clicon_xml_parse.l clicon_xml_parse.tab.h
$(LEX) -Pclicon_xml_parse clicon_xml_parse.l # -d is debug
clicon_xml_parse.tab.c clicon_xml_parse.tab.h: clicon_xml_parse.y
$(YACC) -l -d -p clicon_xml_parse clicon_xml_parse.y # -t is debug
mv y.tab.c clicon_xml_parse.tab.c
mv y.tab.h clicon_xml_parse.tab.h
lex.clicon_xml_parse.o : lex.clicon_xml_parse.c clicon_xml_parse.tab.h # special rule to for make clean to work
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
# clicon_yang parser
lex.clicon_yang_parse.c : clicon_yang_parse.l clicon_yang_parse.tab.h
$(LEX) -Pclicon_yang_parse clicon_yang_parse.l # -d is debug
clicon_yang_parse.tab.c clicon_yang_parse.tab.h: clicon_yang_parse.y
$(YACC) -l -d -p clicon_yang_parse clicon_yang_parse.y # -t is debug
mv y.tab.c clicon_yang_parse.tab.c
mv y.tab.h clicon_yang_parse.tab.h
lex.clicon_yang_parse.o : lex.clicon_yang_parse.c clicon_yang_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
distclean: clean
rm -f Makefile *~ .depend
.SUFFIXES:
.SUFFIXES: .c .o
.c.o: $(GENSRC)
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c $<
.PHONY: build.c
build.c:
date +"const char CLICON_BUILDSTR[64]=\"%Y.%m.%d %H:%M by `whoami` on `hostname`"\"\; > build.c;
echo "const char CLICON_VERSION[64]=\"$(CLICON_VERSION)\""\; >> build.c;
$(MYLIB) : $(GENOBJS) $(OBJS)
$(CC) -shared -Wl,-soname,$(MYLIBSO) -o $@ $(GENOBJS) $(OBJS) $(LIBS) -Wl,-soname=$(MYLIBSO)
# link-name is needed for application linking, eg for clicon_cli and clicon_config
$(MYLIBLINK) : $(MYLIB)
# ln -sf $(MYLIB) $@
# ar cru $@ $^
# ranlib $@
install: install-lib
install-include:
install-lib: $(MYLIB)
install -m 755 -d $(DESTDIR)$(libdir)
install -m 755 $(MYLIB) $(DESTDIR)$(libdir)
ln -sf $(MYLIB) $(DESTDIR)$(libdir)/$(MYLIBSO) # -l:libclicon.so.2
ln -sf $(MYLIBSO) $(DESTDIR)$(libdir)/$(MYLIBLINK) # -l:libclicon.so
uninstall:
rm -f $(libdir)/$(MYLIB)
TAGS:
find . -name '*.[chyl]' -print | etags -
depend:
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend
#include .depend

778
lib/src/clicon_chunk.c Normal file
View file

@ -0,0 +1,778 @@
/*
*
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/>.
*
* Copyright (C) 2002-2011 Benny Holmgren, All rights reserved
*/
/* Error handling: dont use clicon_err, treat as unix system calls. That is,
ensure errno is set and return -1/NULL */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/types.h>
/* clicon */
#include "clicon_queue.h"
#include "clicon_chunk.h"
/*
* The chunk head array for the predefined chunk sizes.
*/
static chunk_head_t chunk_heads[CHUNK_HEADS];
/*
* Did we initialize the chunk heads yet?
*/
static int chunk_initialized = 0;
/*
* The pagesize of this system
*/
static int chunk_pagesz;
/*
* List of chunk groups
*/
static chunk_group_t *chunk_grp;
/*
* Hack to tell unchunk() not to remove chunk_group if empty
*/
static int dont_unchunk_group;
/*
* Initialize chunk library
*/
static void
chunk_initialize ()
{
int pgs;
register int idx;
chunk_pagesz = getpagesize();
bzero (&chunk_heads, sizeof (chunk_heads));
for (idx = 0; idx < CHUNK_HEADS; idx++) {
chunk_head_t *chead = &chunk_heads[idx];
/*
* Calculate the size of a block
*/
pgs = (0x01lu << (CHUNK_BASE + idx)) / chunk_pagesz;
if (pgs == 0)
pgs = 1;
chead->ch_blksz = pgs * chunk_pagesz;
/*
* Chunks per block is 1 for all size above a page. For sizes
* (including the chunk header) less than a page it's as many
* as fits
*/
chead->ch_nchkperblk = chead->ch_blksz / (0x01lu << (CHUNK_BASE + idx));
/*
* Size of each chunk is:
* (size + chnkhdr) * ncnkperblk = blksiz - blkhdr
*/
chead->ch_size =
(chead->ch_blksz / chead->ch_nchkperblk)
- sizeof (chunk_t);
}
/* Zero misc variables */
chunk_grp = NULL;
dont_unchunk_group = 0;
chunk_initialized = 1;
}
/*
* chunk_new_block() - Allocate new block, initialize it and it's chunks.
*/
static int
chunk_new_block (chunk_head_t *chead)
{
register int idx;
register char *c;
chunk_block_t *blk;
chunk_t *cnk;
/* Map block header mem */
blk = (chunk_block_t *)
mmap(NULL, sizeof(chunk_block_t),
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (blk == MAP_FAILED)
return -1;
memset ((void *)blk, 0, sizeof(*blk));
/* Allocate chunk block */
blk->cb_blk = (void *)
mmap(NULL, chead->ch_blksz,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (blk->cb_blk == MAP_FAILED) {
munmap (blk, chead->ch_blksz);
return -1;
}
memset (blk->cb_blk, 0, chead->ch_blksz);
/* Initialize chunk header */
blk->cb_head = chead;
INSQ (blk, chead->ch_blks);
chead->ch_nblks++;
/* Initialize chunks */
c = ((char *)blk->cb_blk);
for (idx = 0; idx < chead->ch_nchkperblk; idx++) {
cnk = (chunk_t *)c;
cnk->c_blk = blk;
INSQ (cnk, chead->ch_free);
chead->ch_nfree++;
c += (chead->ch_size + sizeof (chunk_t));
}
return 0;
}
/*
* chunk_release_block() - Unqueue a block, it's chunks and free mem
*/
static void
chunk_release_block (chunk_block_t *cblk)
{
int idx;
char *c;
chunk_t *cnk;
chunk_head_t *chead;
chead = cblk->cb_head;
/*
* Dequeue block
*/
DELQ (cblk, chead->ch_blks, chunk_block_t *);
chead->ch_nblks--;
/*
* Dequeue all chunks in the block
*/
c = (char *)cblk->cb_blk;
for (idx = 0; idx < chead->ch_nchkperblk; idx++) {
cnk = (chunk_t *)c;
DELQ (cnk, chead->ch_free, chunk_t *);
chead->ch_nfree--;
c += (chead->ch_size + sizeof (chunk_t));
}
/*
* Free block
*/
munmap ((void *)cblk->cb_blk, chead->ch_blksz);
munmap ((void *)cblk, sizeof(*cblk));
}
/*
* chunk_alloc() - Map new chunk of memory
*/
static void *
chunk_alloc (size_t len)
{
register int idx;
chunk_head_t *chead;
chunk_t *cnk;
if (!len)
return (void *)NULL;
/* Find sufficient sized block head */
for (idx = 0; idx < CHUNK_HEADS; idx++)
if (chunk_heads[idx].ch_size >= len)
break;
/* Too large chunk? */
if (idx >= CHUNK_HEADS) {
errno = ENOMEM;
return (void *)NULL;
}
chead = &chunk_heads[idx];
/* Get new block if necessary */
if (!chead->ch_nfree)
if (chunk_new_block (chead))
return (void *)NULL;
/* Move a free chunk to the in-use list */
cnk = chead->ch_free;
DELQ (cnk, chead->ch_free, chunk_t *);
chead->ch_nfree--;
INSQ (cnk, chead->ch_cnks);
/* Add reference to the corresponding block */
cnk->c_blk->cb_ref++;
#ifdef CHUNK_DIAG
/* Clear diag info */
bzero ((void *)&cnk->c_diag, sizeof (cnk->c_diag));
#endif /* CHUNK_DIAG */
/* Return pointer to first byte after the chunk header */
return (void *) (cnk + 1);
}
/*
* chunk() - Map new chunk of memory in group
*/
void *
#ifdef CHUNK_DIAG
_chunk (size_t len, const char *name, const char *file, int line)
#else
chunk (size_t len, const char *name)
#endif
{
int newgrp = 0;
void *ptr = NULL;
chunk_t *cnk;
chunk_group_t *tmp;
chunk_group_t *grp = NULL;
chunk_grpent_t *ent = NULL;
/* Make sure chunk_heads are initialized */
if (!chunk_initialized)
chunk_initialize();
if (!len)
return (void *)NULL;
/* Get actual chunk
*/
ptr = chunk_alloc (len);
if (!ptr)
goto error;
cnk = (chunk_t *) (((char *)ptr) - sizeof (chunk_t));
#ifdef CHUNK_DIAG
/* Store who reuested us
*/
cnk->c_diag.cd_file = file;
cnk->c_diag.cd_line = line;
#endif /* CHUNK_DIAG */
/* No name given. Get an ungrouped chunk
*/
if (!name)
return ptr;
/* Try to find already existing entry
*/
if (chunk_grp) {
tmp = chunk_grp;
do {
if (!strcmp (tmp->cg_name, name)) {
grp = tmp;
break;
}
tmp = NEXTQ(chunk_group_t *, tmp);
} while (tmp != chunk_grp);
}
/* New group.
*/
if ( !grp ) {
grp = (chunk_group_t *) chunk_alloc (sizeof (chunk_group_t));
if (!grp)
goto error;
bzero (grp, sizeof (chunk_group_t));
grp->cg_name = (char *) chunk_alloc (strlen (name) + 1);
if (!grp->cg_name)
goto error;
bcopy (name, grp->cg_name, strlen(name)+1);
newgrp = 1;
}
/* Get new entry.
*/
ent = (chunk_grpent_t *) chunk_alloc (sizeof (chunk_grpent_t));
if (!ent)
goto error;
bzero (ent, sizeof (chunk_grpent_t));
/* Now put everything together
*/
cnk->c_grpent = ent;
ent->ce_cnk = cnk;
ent->ce_grp = grp;
INSQ (ent, grp->cg_ent);
if (newgrp)
INSQ (grp, chunk_grp);
return (ptr);
error:
if (grp && newgrp) {
if (grp->cg_name)
unchunk (grp->cg_name);
unchunk (grp);
}
if (ent)
unchunk (ent);
if (ptr)
unchunk (ptr);
return (void *) NULL;
}
/*
* rechunk() - Resize previously allocated chunk.
*/
void *
#ifdef CHUNK_DIAG
_rechunk (void *ptr, size_t len, const char *name, const char *file, int line)
#else
rechunk (void *ptr, size_t len, const char *name)
#endif
{
int idx;
void *new;
chunk_t *cnk;
chunk_t *newcnk;
chunk_head_t *chead;
chunk_head_t *newchead;
/* No previoud chunk, get new
*/
if (!ptr) {
#ifdef CHUNK_DIAG
return _chunk (len, name, file, line);
#else
return chunk (len, name);
#endif
}
/* Zero length, free chunk
*/
if (len == 0) {
unchunk (ptr);
return (void *) NULL;
}
/* Rewind pointer to beginning of chunk header
*/
cnk = (chunk_t *) (((char *)ptr) - sizeof (chunk_t));
chead = cnk->c_blk->cb_head;
/* Find sufficient sized block head
*/
for (idx = 0; idx < CHUNK_HEADS; idx++)
if (chunk_heads[idx].ch_size >= len)
break;
/* Too large chunk? */
if (idx >= CHUNK_HEADS) {
errno = ENOMEM;
return (void *)NULL;
}
/* Check if chunk size remains unchanged
*/
if (chunk_heads[idx].ch_size == chead->ch_size)
return (ptr);
/* Get new chunk
*/
#ifdef CHUNK_DIAG
new = _chunk (len, name, file, line);
#else
new = chunk (len, name);
#endif
if (!new)
return (void *) NULL;
newcnk = (chunk_t *) (((char *)new) - sizeof (chunk_t));
newchead = newcnk->c_blk->cb_head;
/* Copy contents to new chunk
*/
bcopy (ptr, new, MIN(newchead->ch_size, chead->ch_size));
/* Free old chunk
*/
unchunk (ptr);
return (new);
}
/*
* unchunk() - Release chunk
*/
void
unchunk (void *ptr)
{
chunk_t *cnk;
chunk_head_t *chead;
chunk_block_t *cblk;
chunk_grpent_t *ent;
chunk_group_t *grp;
if (!chunk_initialized)
return;
/* Rewind pointer to beginning of chunk header
*/
cnk = (chunk_t *) (((char *)ptr) - sizeof (chunk_t));
cblk = cnk->c_blk;
chead = cblk->cb_head;
/* Move chunk back to free list
*/
DELQ (cnk, chead->ch_cnks, chunk_t *);
INSQ (cnk, chead->ch_free);
chead->ch_nfree++;
/* If chunk is grouped, remove from group.
*/
ent = cnk->c_grpent;
if (ent) {
grp = ent->ce_grp;
DELQ (ent, grp->cg_ent, chunk_grpent_t *);
unchunk (ent);
cnk->c_grpent = NULL;
/* Group empty? */
if (!dont_unchunk_group && !grp->cg_ent) {
DELQ (grp, chunk_grp, chunk_group_t *);
unchunk(grp->cg_name);
unchunk(grp);
}
}
/* Check block refs is nil, if so free it
*/
cblk->cb_ref--;
if (cblk->cb_ref == 0)
chunk_release_block (cblk);
}
/*
* unchunk_group() - Release all group chunks.
*/
void
unchunk_group (const char *name)
{
chunk_group_t *tmp;
chunk_group_t *grp = NULL;
chunk_t *cnk;
if (!chunk_initialized)
return;
/* Try to find already existing entry
*/
if (chunk_grp) {
tmp = chunk_grp;
do {
if (!strcmp (tmp->cg_name, name)) {
grp = tmp;
break;
}
tmp = NEXTQ(chunk_group_t *, tmp);
} while (tmp != chunk_grp);
}
if (!grp)
return;
/* Walk through all chunks in group an free them
*/
dont_unchunk_group = 1;
while (grp->cg_ent) {
cnk = grp->cg_ent->ce_cnk;
unchunk ((chunk_t *)(((char *)cnk) + sizeof (chunk_t)));
}
dont_unchunk_group = 0;
/* Remove group from list and free it
*/
DELQ (grp, chunk_grp, chunk_group_t *);
unchunk (grp->cg_name);
unchunk (grp);
}
/*
* chunkdup() - Copy block of data to a new chunk of memory in group
*/
void *
#ifdef CHUNK_DIAG
_chunkdup (const void *ptr, size_t len, const char *name, const char *file, int line)
#else
chunkdup (const void *ptr, size_t len, const char *name)
#endif
{
void *new;
/* No input data or no length
*/
if (!ptr || len <= 0)
return (void *)NULL;
/* Get new chunk
*/
#ifdef CHUNK_DIAG
new = _chunk (len, name, file, line);
#else
new = chunk (len, name);
#endif
if (!new)
return (void *)NULL;
/* Copy data to new chunk
*/
memcpy(new, ptr, len);
return (new);
}
/*
* chunksize() - Return size of memory chunk.
*/
size_t
chunksize (void *ptr)
{
chunk_t *cnk;
chunk_head_t *chead;
chunk_block_t *cblk;
if (!chunk_initialized)
return -1;
/* Rewind pointer to beginning of chunk header
*/
cnk = (chunk_t *) (((char *)ptr) - sizeof (chunk_t));
cblk = cnk->c_blk;
chead = cblk->cb_head;
return chead->ch_size;
}
/*
* chunk_strncat() - Concatenate 'n' characters to a chunk allocated string. If
* 'n' is zero, do the whole src string.
*
*/
char *
#ifdef CHUNK_DIAG
_chunk_strncat (const char *dst, const char *src, size_t n, const char *name,
const char *file, int line)
#else
chunk_strncat (const char *dst, const char *src, size_t n, const char *name)
#endif
{
size_t len;
char *new;
void *ptr = (void *)dst;
if (n == 0) /* zero length means cat whole string */
n = strlen(src);
len = (dst ? strlen(dst) : 0) + n + 1;
#ifdef CHUNK_DIAG
ptr = _rechunk(ptr, len, name, file, line);
#else
ptr = rechunk (ptr, len, name);
#endif
if (ptr == NULL)
return NULL;
new = (char *)ptr;
new += strlen(new);
while (n-- > 0 && *src)
*new++ = *src++;
*new = '\0';
return (char *)ptr;
}
/*
* chunk_sprintf() - Format string into new chunk.
*/
char *
#ifdef CHUNK_DIAG
_chunk_sprintf (const char *name, const char *file,
int line, const char *fmt, ...)
#else
chunk_sprintf (const char *name, char *fmt, ...)
#endif
{
size_t len;
char *str;
va_list args;
/* Calculate formatted string length */
va_start(args, fmt);
len = vsnprintf(NULL, 0, fmt, args) + 1;
va_end (args);
/* get chunk */
#ifdef CHUNK_DIAG
str = _chunk (len, name, file, line);
#else
str = chunk (len, name);
#endif
if (str == NULL)
return NULL;
/* Format string */
va_start(args, fmt);
len = vsnprintf(str, len, fmt, args);
va_end (args);
return str;
}
#ifdef CHUNK_DIAG
/*
* chunk_check() - Report all non-freed chunk for given group (if any)
*/
void
chunk_check(FILE *fout, const char *name)
{
int idx;
chunk_t *cnk;
chunk_group_t *tmp;
chunk_group_t *grp = NULL;
chunk_grpent_t *ent;
if (!chunk_initialized)
return;
/* No name given, walk through everything
*/
if (name == (const char *)NULL) {
for (idx = 0; idx < CHUNK_HEADS; idx++) {
chunk_head_t *chead = &chunk_heads[idx];
cnk = chead->ch_cnks;
if (cnk == (chunk_t *)NULL)
continue;
do {
/* If no file name it's an internal chunk */
if (cnk->c_diag.cd_file)
fprintf(fout ? fout : stdout,
"%s:%d,\t%zu bytes (%p), group \"%s\"\n",
cnk->c_diag.cd_file,
cnk->c_diag.cd_line,
cnk->c_blk->cb_head->ch_size,
(cnk +1),
cnk->c_grpent ?
cnk->c_grpent->ce_grp->cg_name :
"NULL");
cnk = NEXTQ(chunk_t *, cnk);
} while (cnk != chead->ch_cnks);
}
}
/* Walk through group
*/
else {
/* Try to find already existing entry
*/
if (chunk_grp) {
tmp = chunk_grp;
do {
if (!strcmp (tmp->cg_name, name)) {
grp = tmp;
break;
}
tmp = NEXTQ(chunk_group_t *, tmp);
} while (tmp != chunk_grp);
}
if (!grp)
return;
ent = grp->cg_ent;
do {
cnk = ent->ce_cnk;
fprintf(fout ? fout : stdout,
"%s:%d,\t%zu bytes (%p), group \"%s\"\n",
cnk->c_diag.cd_file,
cnk->c_diag.cd_line,
cnk->c_blk->cb_head->ch_size,
(cnk +1),
cnk->c_grpent ?
cnk->c_grpent->ce_grp->cg_name :
"NULL");
ent = NEXTQ(chunk_grpent_t *, ent);
} while (ent != grp->cg_ent);
}
}
#endif /* CHUNK_DIAG */

227
lib/src/clicon_err.c Normal file
View file

@ -0,0 +1,227 @@
/*
*
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/>.
*
* Errors may be syslogged using LOG_ERR, and printed to stderr, as controlled
* by clicon_log_init
* global error variables are set:
* clicon_errno, clicon_suberrno, clicon_err_reason.
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include "clicon_log.h"
#include "clicon_queue.h"
#include "clicon_chunk.h"
#include "clicon_err.h"
/*
* Types
*/
struct errvec{
char *ev_str;
int ev_err;
};
struct err_state{
int es_errno;
int es_suberrno;
char es_reason[ERR_STRLEN];
};
/*
* Variables
*/
int clicon_errno = 0; /* See enum clicon_err */
int clicon_suberrno = 0; /* Corresponds to errno.h */
char clicon_err_reason[ERR_STRLEN] = {0, };
/*
* Error descriptions. Must stop with NULL element.
*/
static struct errvec EV[] = {
{"Database error", OE_DB},
{"Demon error", OE_DEMON},
{"Event error", OE_EVENTS},
{"Config error", OE_CFG},
{"Protocol error", OE_PROTO},
{"Regexp error", OE_REGEX},
{"UNIX error", OE_UNIX},
{"Syslog error", OE_SYSLOG},
{"Routing demon error", OE_ROUTING},
{"Plugins", OE_PLUGIN},
{"Yang error", OE_YANG},
{"FATAL", OE_FATAL},
{"Undefined", OE_UNDEF},
{NULL, -1}
};
static char *
clicon_strerror1(int err, struct errvec vec[])
{
struct errvec *ev;
for (ev=vec; ev->ev_err != -1; ev++)
if (ev->ev_err == err)
break;
return ev?(ev->ev_str?ev->ev_str:"unknown"):"CLICON unknown error";
}
/*! Clear error state and continue.
*
* Clear error state and get on with it, typically non-fatal error and you wish to continue.
*/
int
clicon_err_reset(void)
{
clicon_errno = 0;
clicon_suberrno = 0;
memset(clicon_err_reason, 0, ERR_STRLEN);
return 0;
}
/*! Report an error.
*
* Library routines should call this function when an error occurs.
* The function does he following:
* - Logs to syslog with LOG_ERR
* - Set global error variable name clicon_errno
* - Set global reason string clicon_err_reason
* NOTE: err direction (syslog and/or stderr) controlled by clicon_log_init()
*
* @param fn Inline function name (when called from clicon_err() macro)
* @param line Inline file line number (when called from clicon_err() macro)
* @param err Error number, typically errno
* @param suberr Sub-error number
* @param reason Error string, format with argv
*/
int
clicon_err_fn(const char *fn,
const int line,
int category,
int suberr,
char *reason, ...)
{
va_list args;
int len;
char *msg = NULL;
int retval = -1;
/* Set the global variables */
clicon_errno = category;
clicon_suberrno = suberr;
/* first round: compute length of error message */
va_start(args, reason);
len = vsnprintf(NULL, 0, reason, args);
va_end(args);
/* allocate a message string exactly fitting the message length */
if ((msg = malloc(len+1)) == NULL){
fprintf(stderr, "malloc: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
goto done;
}
/* second round: compute write message from reason and args */
va_start(args, reason);
if (vsnprintf(msg, len+1, reason, args) < 0){
va_end(args);
fprintf(stderr, "vsnprintf: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
goto done;
}
va_end(args);
strncpy(clicon_err_reason, msg, ERR_STRLEN-1);
/* Actually log it */
if (suberr){
/* Here we could take care of specific suberr, like application-defined errors */
clicon_log(LOG_ERR, "%s: %d: %s: %s: %s",
fn,
line,
clicon_strerror(category),
msg,
strerror(suberr));
}
else
clicon_log(LOG_ERR, "%s: %d: %s: %s",
fn,
line,
clicon_strerror(category),
msg);
retval = 0;
done:
if (msg)
free(msg);
return retval;
}
/*! Translate from numeric error to string representation
*/
char *
clicon_strerror(int err)
{
return clicon_strerror1(err, EV);
}
/*! Push an error state, if recursive error handling
*/
void*
clicon_err_save(void)
{
struct err_state *es;
if ((es = chunk(sizeof(*es), NULL)) == NULL)
return NULL;
es->es_errno = clicon_errno;
es->es_suberrno = clicon_suberrno;
strncpy(es->es_reason, clicon_err_reason, ERR_STRLEN-1);
return (void*)es;
}
/*! Pop an error state, if recursive error handling
*/
int
clicon_err_restore(void* handle)
{
struct err_state *es;
es = (struct err_state *)handle;
clicon_errno = es->es_errno;
clicon_suberrno = es->es_suberrno;
strncpy(clicon_err_reason, es->es_reason, ERR_STRLEN-1);
unchunk(es);
return 0;
}

309
lib/src/clicon_event.c Normal file
View file

@ -0,0 +1,309 @@
/*
*
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/>.
*
* Event handling and loop
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/time.h>
#include "clicon_queue.h"
#include "clicon_log.h"
#include "clicon_err.h"
#include "clicon_event.h"
/*
* Constants
*/
#define EVENT_STRLEN 32
/*
* Types
*/
struct event_data{
struct event_data *e_next; /* next in list */
int (*e_fn)(int, void*); /* function */
enum {EVENT_FD, EVENT_TIME} e_type; /* type of event */
int e_fd; /* File descriptor */
struct timeval e_time; /* Timeout */
void *e_arg; /* function argument */
char e_string[EVENT_STRLEN]; /* string for debugging */
};
/*
* Internal variables
*/
static struct event_data *ee = NULL;
static struct event_data *ee_timers = NULL;
/* Set if element in ee is deleted (event_unreg_fd). Check in ee loops */
static int _ee_unreg = 0;
static int _clicon_exit = 0;
/*! For signal handlers: instead of doing exit, set a global variable to exit
* Status is then checked in event_loop.
* Note it maybe would be better to do use on a handle basis, bit a signal
* handler is global
*/
int
clicon_exit_set(void)
{
_clicon_exit++;
return 0;
}
/*! Get the status of global exit variable, usually set by signal handlers
*/
int
clicon_exit_get(void)
{
return _clicon_exit;
}
/*! Register a callback function to be called on input on a file descriptor.
*
* @param[in] fd File descriptor
* @param[in] fn Function to call when input available on fd
* @param[in] arg Argument to function fn
* @param[in] str Describing string for logging
* @code
* int fn(int fd, void *arg){
* }
* event_reg_fd(fd, fn, (void*)42, "call fn on input on fd");
* @endcode
*/
int
event_reg_fd(int fd, int (*fn)(int, void*), void *arg, char *str)
{
struct event_data *e;
if ((e = (struct event_data *)malloc(sizeof(struct event_data))) == NULL){
clicon_err(OE_EVENTS, errno, "malloc");
return -1;
}
memset(e, 0, sizeof(struct event_data));
strncpy(e->e_string, str, EVENT_STRLEN);
e->e_fd = fd;
e->e_fn = fn;
e->e_arg = arg;
e->e_type = EVENT_FD;
e->e_next = ee;
ee = e;
clicon_debug(2, "%s, registering %s", __FUNCTION__, e->e_string);
return 0;
}
/*! Deregister a file descriptor callback
* @param[in] s File descriptor
* @param[in] fn Function to call when input available on fd
* Note: deregister when exactly function and socket match, not argument
* @see event_reg_fd
* @see event_unreg_timeout
*/
int
event_unreg_fd(int s, int (*fn)(int, void*))
{
struct event_data *e, **e_prev;
int found = 0;
e_prev = &ee;
for (e = ee; e; e = e->e_next){
if (fn == e->e_fn && s == e->e_fd) {
found++;
*e_prev = e->e_next;
_ee_unreg++;
free(e);
break;
}
e_prev = &e->e_next;
}
return found?0:-1;
}
/*! Call a callback function at an absolute time
* @param[in] t Absolute (not relative!) timestamp when callback is called
* @param[in] fn Function to call at time t
* @param[in] arg Argument to function fn
* @param[in] str Describing string for logging
* @code
* int fn(int d, void *arg){
* struct timeval t, t1;
* gettimeofday(&t, NULL);
* t1.tv_sec = 1; t1.tv_usec = 0;
* timeradd(&t, &t1, &t);
* event_reg_timeout(t, fn, NULL, "call every second");
* }
* @endcode
*
* Note that the timestamp is an absolute timestamp, not relative.
* Note also that the callback is not periodic, you need to make a new
* registration for each period, see example above.
* Note also that the first argument to fn is a dummy, just to get the same
* signatute as for file-descriptor callbacks.
* @see event_reg_fd
* @see event_unreg_timeout
*/
int
event_reg_timeout(struct timeval t, int (*fn)(int, void*),
void *arg, char *str)
{
struct event_data *e, *e1, **e_prev;
if ((e = (struct event_data *)malloc(sizeof(struct event_data))) == NULL){
clicon_err(OE_EVENTS, errno, "malloc");
return -1;
}
memset(e, 0, sizeof(struct event_data));
strncpy(e->e_string, str, EVENT_STRLEN);
e->e_fn = fn;
e->e_arg = arg;
e->e_type = EVENT_TIME;
e->e_time = t;
/* Sort into right place */
e_prev = &ee_timers;
for (e1=ee_timers; e1; e1=e1->e_next){
if (timercmp(&e->e_time, &e1->e_time, <))
break;
e_prev = &e1->e_next;
}
e->e_next = e1;
*e_prev = e;
clicon_debug(2, "event_reg_timeout: %s", str);
return 0;
}
/*! Deregister a timeout callback as previosly registered by event_reg_timeout()
* Note: deregister when exactly function and function arguments match, not time. So you
* cannot have same function and argument callback on different timeouts. This is a little
* different from event_unreg_fd.
* @param[in] fn Function to call at time t
* @param[in] arg Argument to function fn
* @see event_reg_timeout
* @see event_unreg_fd
*/
int
event_unreg_timeout(int (*fn)(int, void*), void *arg)
{
struct event_data *e, **e_prev;
int found = 0;
e_prev = &ee_timers;
for (e = ee_timers; e; e = e->e_next){
if (fn == e->e_fn && arg == e->e_arg) {
found++;
*e_prev = e->e_next;
free(e);
break;
}
e_prev = &e->e_next;
}
return found?0:-1;
}
/*! Dispatch file descriptor events (and timeouts) by invoking callbacks.
* There is an issue with fairness that timeouts may take over all events
* One could try to poll the file descriptors after a timeout?
*/
int
event_loop(void)
{
struct event_data *e, *e_next;
int n;
struct timeval t, t0, tnull={0,};
fd_set fdset;
int retval = -1;
while (!clicon_exit_get()){
FD_ZERO(&fdset);
for (e=ee; e; e=e->e_next)
if (e->e_type == EVENT_FD)
FD_SET(e->e_fd, &fdset);
if (ee_timers != NULL){
gettimeofday(&t0, NULL);
timersub(&ee_timers->e_time, &t0, &t);
if (t.tv_sec < 0)
n = select(FD_SETSIZE, &fdset, NULL, NULL, &tnull);
else
n = select(FD_SETSIZE, &fdset, NULL, NULL, &t);
}
else
n = select(FD_SETSIZE, &fdset, NULL, NULL, NULL);
if (clicon_exit_get())
break;
if (n == -1) {
if (errno == EINTR){
clicon_debug(1, "%s select: %s", __FUNCTION__, strerror(errno));
clicon_err(OE_EVENTS, errno, "%s select1: %s", __FUNCTION__, strerror(errno));
retval = 0;
goto err;
}
else
clicon_err(OE_EVENTS, errno, "%s select2", __FUNCTION__);
goto err;
}
if (n==0){ /* Timeout */
e = ee_timers;
ee_timers = ee_timers->e_next;
clicon_debug(2, "%s timeout: %s[%x]",
__FUNCTION__, e->e_string, e->e_arg);
if ((*e->e_fn)(0, e->e_arg) < 0){
free(e);
goto err;
}
free(e);
}
_ee_unreg = 0;
for (e=ee; e; e=e_next){
if (clicon_exit_get())
break;
e_next = e->e_next;
if(e->e_type == EVENT_FD && FD_ISSET(e->e_fd, &fdset)){
clicon_debug(2, "%s: FD_ISSET: %s[%x]",
__FUNCTION__, e->e_string, e->e_arg);
if ((*e->e_fn)(e->e_fd, e->e_arg) < 0)
goto err;
if (_ee_unreg){
_ee_unreg = 0;
break;
}
}
}
continue;
err:
break;
}
clicon_debug(1, "%s done:", __FUNCTION__);
return retval;
}

348
lib/src/clicon_file.c Normal file
View file

@ -0,0 +1,348 @@
/*
*
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"
#endif
#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU /* strverscmp */
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <regex.h>
#include <pwd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <unistd.h>
#include <netinet/in.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clicon_err.h"
#include "clicon_queue.h"
#include "clicon_chunk.h"
#include "clicon_string.h"
#include "clicon_file.h"
/*
* Resolve the real path of a given 'path', following symbolic links and '../'.
* If 'path' relative, it will be resolved based on the currnt working
* directory 'cwd'. The response is a 2 entry vector of strings. The first
* entry is the resolved path and the second is the part of the path which
* actually exist.
*/
char **
clicon_realpath(const char *cwd, char *path, const char *label)
{
char **ret = NULL;
char *rest;
char **vec, **vec2;
int nvec, nvec2;
char *p;
char *rp = NULL;
char *ptr;
int i;
struct passwd *pwd;
char cwdbuf[PATH_MAX];
/* Prepend 'cwd' if not absolute */
if (path[0] == '/')
p = path;
else {
if (cwd == NULL || strlen(cwd) == 0)
cwd = getcwd(cwdbuf, sizeof(cwdbuf));
else if (cwd[0] == '~') {
if((pwd = getpwuid(getuid())) == NULL)
goto catch;
cwd = pwd->pw_dir;
}
p = chunk_sprintf(__FUNCTION__, "%s%s/%s",
(cwd[0]=='/' ? "" : "/"), cwd, path);
}
if (p == NULL)
goto catch;
/* Make a local copy of 'path' */
if ((path = chunkdup(p, strlen(p)+1, __FUNCTION__)) == NULL)
goto catch;
/* Find the smallest portion of the path that exist and run realpath() */
while(strlen(p) && ((rp = realpath(p, NULL)) == NULL)) {
if((ptr = strrchr(p, '/')) == NULL)
break;
*ptr = '\0';
}
if(rp == NULL)
goto catch;
/* Use the result of realpath() and the rest of 'path' untouched, to
form a new path */
rest = path + strlen(p);
ptr = chunk_sprintf(__FUNCTION__, "%s%s", rp, rest);
p = ptr;
/* Split path based on '/'. Loop through vector from the end and copy
each entry into a new vector, skipping '..' and it's previous directory
as well as all '.' */
vec = clicon_strsplit (p, "/", &nvec, __FUNCTION__);
vec2 = chunk(nvec * sizeof(char *), __FUNCTION__);
nvec2 = i = nvec;
while(--i >= 0) {
if(strcmp(vec[i], "..") == 0)
i--; /* Skip previous */
else if(strcmp(vec[i], ".") == 0)
/* do nothing */ ;
else
vec2[--nvec2] = vec[i];
}
/* Create resulting vector */
if ((ret = chunk(sizeof(char *) * 2, label)) != NULL) {
if((ret[0] = clicon_strjoin(nvec-nvec2, &vec2[nvec2], "/", label)) == NULL) {
unchunk(ret);
ret = NULL;
}
if ((ret[1] = chunkdup(rp, strlen(rp)+1, label)) == NULL) {
unchunk(ret[0]);
unchunk(ret);
ret = NULL;
}
}
catch:
if(rp)
free(rp);
unchunk_group(__FUNCTION__);
return ret;
}
/*
* qsort function
*/
static int
clicon_file_dirent_sort(const void* arg1, const void* arg2)
{
struct dirent *d1 = (struct dirent *)arg1;
struct dirent *d2 = (struct dirent *)arg2;
#ifdef HAVE_STRVERSCMP
return strverscmp(d1->d_name, d2->d_name); /* strverscmp specific GNU function */
#else /* HAVE_STRVERSCMP */
return strcoll(d1->d_name, d2->d_name);
#endif /* HAVE_STRVERSCMP */
}
/*! Return sorted matching files from a directory
* @param[in] dir Directory path
* @param[out] ent Entries pointer, will be filled in with dir entries
* @param[in] regexp Regexp filename matching
* @param[in] type File type matching, see stat(2)
* @param[in] label Clicon Chunk label for memory handling, unchunk after use
*
* @retval n Number of matching files in directory
* @retval -1 Error
*
* @code
* char *dir = "/root/fs";
* struct dirent *dp;
* if ((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG, __FUNCTION__)) < 0)
* return -1;
* for (i = 0; i < ndp; i++)
* do something with dp[i].d_name;
* unchunk_group(__FUNCTION__);
* @endcode
*/
int
clicon_file_dirent(const char *dir,
struct dirent **ent,
const char *regexp,
mode_t type,
const char *label)
{
DIR *dirp;
int retval = -1;
int res;
int nent;
char *filename;
regex_t re;
char errbuf[128];
struct stat st;
struct dirent dent;
struct dirent *dresp;
struct dirent *tmp;
struct dirent *new = NULL;
struct dirent *dvecp = NULL;
*ent = NULL;
nent = 0;
if (regexp && (res = regcomp(&re, regexp, REG_EXTENDED)) != 0) {
regerror(res, &re, errbuf, sizeof(errbuf));
clicon_err(OE_DB, 0, "regcomp: %s", errbuf);
return -1;
}
if ((dirp = opendir (dir)) == NULL) {
if (errno == ENOENT) /* Dir does not exist -> return 0 matches */
retval = 0;
else
clicon_err(OE_UNIX, errno, "opendir(%s)", dir);
goto quit;
}
for (res = readdir_r (dirp, &dent, &dresp); dresp; res = readdir_r (dirp, &dent, &dresp)) {
if (res != 0) {
clicon_err(OE_UNIX, 0, "readdir: %s", strerror(errno));
goto quit;
}
/* Filename matching */
if (regexp) {
if (regexec(&re, dent.d_name, (size_t) 0, NULL, 0) != 0)
continue;
}
/* File type matching */
if (type) {
if ((filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dent.d_name)) == NULL) {
clicon_err(OE_UNIX, 0, "chunk: %s", strerror(errno));
goto quit;
}
res = lstat (filename, &st);
unchunk (filename);
if (res != 0) {
clicon_err(OE_UNIX, 0, "lstat: %s", strerror(errno));
goto quit;
}
if ((type & st.st_mode) == 0)
continue;
}
if ((tmp = rechunk(new, (nent+1)*sizeof(*dvecp), label)) == NULL) {
clicon_err(OE_UNIX, 0, "chunk: %s", strerror(errno));
goto quit;
}
new = tmp;
memcpy (&new[nent], &dent, sizeof(dent));
nent++;
} /* while */
qsort((void *)new, nent, sizeof(*new), clicon_file_dirent_sort);
*ent = new;
retval = nent;
quit:
if (dirp)
closedir(dirp);
if (regexp)
regfree(&re);
unchunk_group(__FUNCTION__);
return retval;
}
/*
* Use mkstep() to create an empty temporary file, accessible only by this user.
* A chunk:ed file name is returned so that caller can overwrite file safely.
*/
char *
clicon_tmpfile(const char *label)
{
int fd;
char file[] = "/tmp/.tmpXXXXXX";
if ((fd = mkstemp(file)) < 0){
clicon_err(OE_UNIX, errno, "mkstemp");
return NULL;
}
close(fd);
return (char *)chunkdup(file, strlen(file)+1, label);
}
/*
* Make a copy of file src
* On error returns -1 and sets errno.
*/
int
file_cp(char *src, char *target)
{
int inF = 0, ouF = 0;
int err = 0;
char line[512];
int bytes;
struct stat st;
int retval = -1;
if (stat(src, &st) != 0)
return -1;
if((inF = open(src, O_RDONLY)) == -1)
return -1;
if((ouF = open(target, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode)) == -1) {
err = errno;
goto error;
}
while((bytes = read(inF, line, sizeof(line))) > 0)
if (write(ouF, line, bytes) < 0){
err = errno;
goto error;
}
retval = 0;
error:
close(inF);
if (ouF)
close(ouF);
if (retval < 0)
errno = err;
return retval;
}
#ifdef NOTUSED
/*
* (un)lock a whole file.
* Arguments:
* fd - File descriptor
* cmd - F_GETLK, F_SETLK, F_SETLKW
* type - F_RDLCK, F_WRLCK, F_UNLCK
*/
int
file_lock(int fd, int cmd, int type)
{
struct flock lock;
lock.l_type = type;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
return fcntl(fd, cmd, &lock);
}
#endif

155
lib/src/clicon_handle.c Normal file
View file

@ -0,0 +1,155 @@
/*
*
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"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <cligen/cligen.h>
/* clicon */
#include "clicon_queue.h"
#include "clicon_hash.h"
#include "clicon_handle.h"
#include "clicon_err.h"
#include "clicon_yang.h"
#include "clicon_options.h"
#define CLICON_MAGIC 0x99aafabe
#define handle(h) (assert(clicon_handle_check(h)==0),(struct clicon_handle *)(h))
/*
* clicon_handle
* Internal structire of basic handle. Also header of all other handles.
* see struct clicon_cli_handle, struct clicon_backend_handle, etc
*/
struct clicon_handle {
int ch_magic; /* magic (HDR) */
clicon_hash_t *ch_copt; /* clicon option list (HDR) */
clicon_hash_t *ch_data; /* internal clicon data (HDR) */
};
/*! Internal call to allocate a CLICON handle.
*
* There may be different variants of handles with some common options.
* So far the only common options is a MAGIC cookie for sanity checks and
* CLICON options
*/
clicon_handle
clicon_handle_init0(int size)
{
struct clicon_handle *ch;
clicon_handle h = NULL;
if ((ch = malloc(size)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(ch, 0, size);
ch->ch_magic = CLICON_MAGIC;
if ((ch->ch_copt = hash_init()) == NULL){
clicon_handle_exit((clicon_handle)ch);
goto done;
}
if ((ch->ch_data = hash_init()) == NULL){
clicon_handle_exit((clicon_handle)ch);
goto done;
}
h = (clicon_handle)ch;
done:
return h;
}
/*! Basic CLICON init functions returning a handle for API access.
*
* This is the first call to CLICON basic API which returns a handle to be
* used in the API functions. There are other clicon_init functions for more
* elaborate applications (cli/backend/netconf). This should be used by the most
* basic applications that use CLICON lib directly.
*/
clicon_handle
clicon_handle_init(void)
{
return clicon_handle_init0(sizeof(struct clicon_handle));
}
/*! Deallocate clicon handle, including freeing handle data.
* @Note: handle 'h' cannot be used in calls after this
*/
int
clicon_handle_exit(clicon_handle h)
{
struct clicon_handle *ch = handle(h);
clicon_hash_t *copt;
clicon_hash_t *data;
if ((copt = clicon_options(h)) != NULL)
hash_free(copt);
if ((data = clicon_data(h)) != NULL)
hash_free(data);
free(ch);
return 0;
}
/*
* Check struct magic number for sanity checks
* return 0 if OK, -1 if fail.
*/
int
clicon_handle_check(clicon_handle h)
{
/* Dont use handle macro to avoid recursion */
struct clicon_handle *ch = (struct clicon_handle *)(h);
return ch->ch_magic == CLICON_MAGIC ? 0 : -1;
}
/*
* Return clicon options (hash-array) given a handle.
*/
clicon_hash_t *
clicon_options(clicon_handle h)
{
struct clicon_handle *ch = handle(h);
return ch->ch_copt;
}
/*
* Return clicon data (hash-array) given a handle.
*/
clicon_hash_t *
clicon_data(clicon_handle h)
{
struct clicon_handle *ch = handle(h);
return ch->ch_data;
}

343
lib/src/clicon_hash.c Normal file
View file

@ -0,0 +1,343 @@
/*
*
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/>.
*/
/*
* A simple implementation of a associative array style data store. Keys
* are always strings while values can be some arbitrary data referenced
* by void*.
*
* XXX: functions such as hash_keys(), hash_value() etc are currently returning
* pointers to the actual data storage. Should probably make copies.
*
* Example usage:
*
* int main()
* {
* char **s;
* int n;
* size_t slen;
* clicon_hash_t *hash = hash_init();
*
* n = 234;
* hash_add (hash, "APA", &n, sizeof(n));
* hash_dump(hash, stdout);
*
* puts("----");
*
* hash_add (hash, "BEPA", "hoppla Polle!", strlen("hoppla Polle!")+1);
* puts((char *)hash_value(hash, "BEPA", NULL));
* hash_dump(hash, stdout);
*
* puts("----");
*
* n = 33;
* hash_add (hash, "CEPA", &n, sizeof(n));
* hash_dump(hash, stdout);
*
* puts("----");
*
* hash_del (hash, "APA");
* hash_dump(hash, stdout);
*
* hash_free(hash);
*
* return 0;
* }
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>
/* clicon */
#include "clicon_queue.h"
#include "clicon_err.h"
#include "clicon_hash.h"
#define HASH_SIZE 1031 /* Number of hash buckets. Should be a prime */
/*
* A very simplistic algorithm to calculate a hash bucket index
*/
static uint32_t
hash_bucket(const char *str)
{
uint32_t n = 0;
while(*str)
n += (uint32_t)*str++;
return n % HASH_SIZE;
}
/*
* Initialize hash table.
*
* Arguments: none
*
* Returns: new pointer to hash table.
*/
clicon_hash_t *
hash_init (void)
{
clicon_hash_t *hash;
if ((hash = (clicon_hash_t *)malloc (sizeof (clicon_hash_t) * HASH_SIZE)) == NULL){
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
return NULL;
}
memset (hash, 0, sizeof(clicon_hash_t)*HASH_SIZE);
return hash;
}
/*
* Free hash table.
*
* Arguments:
* hash - Hash table
*
* Returns: void
*/
void
hash_free (clicon_hash_t *hash)
{
int i;
clicon_hash_t tmp;
for (i = 0; i < HASH_SIZE; i++) {
while (hash[i]) {
tmp = hash[i];
DELQ(tmp, hash[i], clicon_hash_t);
free(tmp->h_key);
free(tmp->h_val);
free(tmp);
}
}
free(hash);
}
/*
* Find keys.
*
* key - Variable name
*
* Returns: variable structure on success, NULL on failure
*/
clicon_hash_t
hash_lookup (clicon_hash_t *hash, const char *key)
{
uint32_t bkt;
clicon_hash_t h;
bkt = hash_bucket(key);
h = hash[bkt];
if (h) {
do {
if (!strcmp (h->h_key, key))
return h;
h = NEXTQ(clicon_hash_t, h);
} while (h != hash[bkt]);
}
return NULL;
}
/*
* Get value of hash
*/
void *
hash_value(clicon_hash_t *hash, const char *key, size_t *vlen)
{
clicon_hash_t h;
h = hash_lookup(hash, key);
if (h == NULL)
return NULL;
if (vlen)
*vlen = h->h_vlen;
return h->h_val;
}
/*
* Copy value and add hash entry.
*
* Arguments:
* hash - Hash structure
* key - New variable name
* val - New variable value
*
* Returns: new variable on success, NULL on failure
*/
clicon_hash_t
hash_add (clicon_hash_t *hash, const char *key, void *val, size_t vlen)
{
void *newval;
clicon_hash_t h, new = NULL;
/* If variable exist, don't allocate a new. just replace value */
h = hash_lookup (hash, key);
if (h == NULL) {
if ((new = (clicon_hash_t)malloc (sizeof (*new))) == NULL){
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
goto catch;
}
memset (new, 0, sizeof (*new));
new->h_key = strdup (key);
if (new->h_key == NULL){
clicon_err(OE_UNIX, errno, "strdup: %s", strerror(errno));
goto catch;
}
h = new;
}
/* Make copy of lvalue */
newval = malloc (vlen+3); /* XXX: qdbm needs aligned mallocs? */
if (newval == NULL){
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
goto catch;
}
memcpy (newval, val, vlen);
/* Free old value if existing variable */
if (h->h_val)
free (h->h_val);
h->h_val = newval;
h->h_vlen = vlen;
/* Add to list only if new variable */
if (new)
INSQ(h, hash[hash_bucket(key)]);
return h;
catch:
if (new) {
if (new->h_key)
free (new->h_key);
free (new);
}
return NULL;
}
/*
* Delete entry.
*
* Arguments:
* hash - Hash structure
* key - Variable name
*
* Returns: 0 on success, -1 on failure
*/
int
hash_del (clicon_hash_t *hash, const char *key)
{
clicon_hash_t h;
h = hash_lookup (hash, key);
if (h == NULL)
return -1;
DELQ(h, hash[hash_bucket(key)], clicon_hash_t);
free (h->h_key);
free (h->h_val);
free (h);
return 0;
}
char **
hash_keys(clicon_hash_t *hash, size_t *nkeys)
{
int bkt;
clicon_hash_t h;
char **tmp;
char **keys = NULL;
*nkeys = 0;
for (bkt = 0; bkt < HASH_SIZE; bkt++) {
h = hash[bkt];
do {
if (h == NULL)
break;
tmp = realloc(keys, ((*nkeys)+1) * sizeof(char *));
if (tmp == NULL){
clicon_err(OE_UNIX, errno, "realloc: %s", strerror(errno));
goto catch;
}
keys = tmp;
keys[*nkeys] = h->h_key;
(*nkeys)++;
h = NEXTQ(clicon_hash_t, h);
} while (h != hash[bkt]);
}
return keys;
catch:
if (keys)
free(keys);
return NULL;
}
/*
* Dump contents of hash to FILE pointer.
*
* Arguments:
* f - FILE pointer for print output
* hash - Hash structure
*
* Returns: void
*/
void
hash_dump(clicon_hash_t *hash, FILE *f)
{
int i;
char **keys;
void *val;
size_t klen;
size_t vlen;
if (hash == NULL)
return;
keys = hash_keys(hash, &klen);
if (keys == NULL)
return;
for(i = 0; i < klen; i++) {
val = hash_value(hash, keys[i], &vlen);
printf("%s =\t 0x%p , length %zu\n", keys[i], val, vlen);
}
free(keys);
}

332
lib/src/clicon_log.c Normal file
View file

@ -0,0 +1,332 @@
/*
*
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/>.
*
* Regular logging and debugging. Syslog using levels.
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
/* clicon */
#include "clicon_err.h"
#include "clicon_log.h"
/* The global debug level. 0 means no debug */
int debug = 0;
/* Bitmask whether to log to syslog or stderr: CLICON_LOG_STDERR | CLICON_LOG_SYSLOG */
static int _logflags = 0x0;
/* Function pointer to log notify callback */
static clicon_log_notify_t *_log_notify_cb = NULL;
static void *_log_notify_arg = NULL;
/* Set to open file to bypass logging and write debug messages directly to file */
static FILE *_debugfile = NULL;
/*! Initialize system logger.
*
* Make syslog(3) calls with specified ident and gates calls of level upto specified level (upto).
* May also print to stderr, if err is set.
* Applies to clicon_err() and clicon_debug too
*
* @param[in] ident prefix that appears on syslog (eg 'cli')
* @param[in] upto log priority, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG (see syslog(3)).
* @param[in] flags bitmask: if CLICON_LOG_STDERR, then print logs to stderr
if CLICON_LOG_SYSLOG, then print logs to syslog
You can do a combination of both
*/
int
clicon_log_init(char *ident, int upto, int flags)
{
if (setlogmask(LOG_UPTO(upto)) < 0)
/* Cant syslog here */
fprintf(stderr, "%s: setlogmask: %s\n", __FUNCTION__, strerror(errno));
_logflags = flags;
openlog(ident, LOG_PID, LOG_USER); /* LOG_PUSER is achieved by direct stderr logs in clicon_log */
return 0;
}
/*! Register log callback, return old setting
*/
clicon_log_notify_t *
clicon_log_register_callback(clicon_log_notify_t *cb, void *arg)
{
clicon_log_notify_t *old = _log_notify_cb;
_log_notify_cb = cb;
_log_notify_arg = arg;
return old;
}
/*
* Mimic syslog and print a time on file f
*/
static int
flogtime(FILE *f)
{
struct timeval tv;
struct tm *tm;
gettimeofday(&tv, NULL);
tm = localtime((time_t*)&tv.tv_sec);
fprintf(f, "%s %2d %02d:%02d:%02d: ",
mon2name(tm->tm_mon), tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
return 0;
}
/*
* Mimic syslog and print a time on string s
* String returned needs to be freed.
*/
static char *
slogtime(void)
{
struct timeval tv;
struct tm *tm;
char *str;
/* Example: "Apr 14 11:30:52: " len=17+1 */
if ((str = malloc(18)) == NULL){
fprintf(stderr, "%s: malloc: %s\n", __FUNCTION__, strerror(errno));
return NULL;
}
gettimeofday(&tv, NULL);
tm = localtime((time_t*)&tv.tv_sec);
snprintf(str, 18, "%s %2d %02d:%02d:%02d: ",
mon2name(tm->tm_mon), tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
return str;
}
/*! Make a logging call to syslog.
*
* This is the _only_ place the actual syslog (or stderr) logging is made in clicon,..
* See also clicon_log()
*
* @param[in] level log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG. Thisis OR:d with facility == LOG_USER
* @param[in] msg Message to print as argv.
*/
int
clicon_log_str(int level, char *msg)
{
if (_logflags & CLICON_LOG_SYSLOG)
syslog(LOG_MAKEPRI(LOG_USER, level), "%s", msg);
if (_logflags & CLICON_LOG_STDERR){
flogtime(stderr);
fprintf(stderr, "%s\n", msg);
}
if (_logflags & CLICON_LOG_STDOUT){
flogtime(stdout);
fprintf(stdout, "%s\n", msg);
}
if (_log_notify_cb){
static int cb = 0;
char *d, *msg2;
int len;
if (cb++ == 0){
/* Here there is danger of recursion: if callback in turn logs, therefore
make static check (should be stack-based - now global)
*/
if ((d = slogtime()) == NULL)
return -1;
len = strlen(d) + strlen(msg) + 1;
if ((msg2 = malloc(len)) == NULL){
fprintf(stderr, "%s: malloc: %s\n", __FUNCTION__, strerror(errno));
return -1;
}
snprintf(msg2, len, "%s%s", d, msg);
assert(_log_notify_arg);
_log_notify_cb(level, msg2, _log_notify_arg);
free(d);
free(msg2);
}
cb--;
}
return 0;
}
/*! Make a logging call to syslog using variable arg syntax.
*
* See also clicon_log_init() and clicon_log_str()
*
* @code
clicon_log(LOG_NOTICE, "%s: dump to dtd not supported", __PROGRAM__);
* @endcode
* @param[in] level log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG. Thisis OR:d with facility == LOG_USER
* @param[in] format Message to print as argv.
*/
int
clicon_log(int level, char *format, ...)
{
va_list args;
int len;
char *msg = NULL;
int retval = -1;
/* first round: compute length of debug message */
va_start(args, format);
len = vsnprintf(NULL, 0, format, args);
va_end(args);
/* allocate a message string exactly fitting the message length */
if ((msg = malloc(len+1)) == NULL){
fprintf(stderr, "malloc: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
goto done;
}
/* second round: compute write message from format and args */
va_start(args, format);
if (vsnprintf(msg, len+1, format, args) < 0){
va_end(args);
fprintf(stderr, "vsnprintf: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
goto done;
}
va_end(args);
/* Actually log it */
clicon_log_str(level, msg);
retval = 0;
done:
if (msg)
free(msg);
return retval;
}
/*! Initialize debug messages. Set debug level.
*
* Initialize debug module. The level is used together with clicon_debug(dbglevel) calls as follows:
* print message if level >= dbglevel.
* Example: clicon_debug_init(1) -> debug(1) is printed, but not debug(2).
* Normally, debug messages are sent to clicon_log() which in turn can be sent to syslog and/or stderr.
* But you can also override this with a specific debug file so that debug messages are written on the file
* independently of log or errors. This is to ensure that a syslog of normal logs is unpolluted by extensive
* debugging.
*
* @param[in] dbglevel 0 is show no debug messages, 1 is normal, 2.. is high debug.
Note this is _not_ level from syslog(3)
* @param[in] f Debug-file. Open file where debug messages are directed.
This overrides the clicon_log settings which is otherwise where
debug messages are directed.
*/
int
clicon_debug_init(int dbglevel, FILE *f)
{
debug = dbglevel; /* Global variable */
return 0;
}
/*! Print a debug message with debug-level. Settings determine where msg appears.
*
* If the dbglevel passed in the function is equal to or lower than the one set by
* clicon_debug_init(level). That is, only print debug messages <= than what you want:
* print message if level >= dbglevel.
* The message is sent to clicon_log. EIther to syslog, stderr or both, depending on
* clicon_log_init() setting
*
* @param[in] dbglevel 0 always called (dont do this: not really a dbg message)
* 1 default level if passed -D
* 2.. Higher debug levels
* @param[in] format Message to print as argv.
*/
int
clicon_debug(int dbglevel, char *format, ...)
{
va_list args;
int len;
char *msg = NULL;
int retval = -1;
if (dbglevel > debug) /* debug mask */
return 0;
/* first round: compute length of debug message */
va_start(args, format);
len = vsnprintf(NULL, 0, format, args);
va_end(args);
/* allocate a message string exactly fitting the messgae length */
if ((msg = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: compute write message from format and args */
va_start(args, format);
if (vsnprintf(msg, len+1, format, args) < 0){
va_end(args);
clicon_err(OE_UNIX, errno, "vsnprintf");
goto done;
}
va_end(args);
if (_debugfile != NULL){ /* Bypass syslog altogether */
/* XXX: Here use date sub-routine as found in err_print1 */
flogtime(_debugfile);
fprintf(_debugfile, "%s\n", msg);
}
else
clicon_log_str(LOG_DEBUG, msg);
retval = 0;
done:
if (msg)
free(msg);
return retval;
}
/*! Translate month number (0..11) to a three letter month name
*/
char *
mon2name(int md)
{
switch(md){
case 0: return "Jan";
case 1: return "Feb";
case 2: return "Mar";
case 3: return "Apr";
case 4: return "May";
case 5: return "Jun";
case 6: return "Jul";
case 7: return "Aug";
case 8: return "Sep";
case 9: return "Oct";
case 10: return "Nov";
case 11: return "Dec";
default:
break;
}
return NULL;
}

684
lib/src/clicon_options.c Normal file
View file

@ -0,0 +1,684 @@
/*
*
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/>.
*
* CLICON options
* See clicon_tutorial appendix and clicon.conf.cpp.cpp on documentation of
* options
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/socket.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clicon_err.h"
#include "clicon_queue.h"
#include "clicon_hash.h"
#include "clicon_handle.h"
#include "clicon_chunk.h"
#include "clicon_log.h"
#include "clicon_yang.h"
#include "clicon_options.h"
/*
* clicon_option_dump
* Print registry on file. For debugging.
*/
void
clicon_option_dump(clicon_handle h, int dbglevel)
{
clicon_hash_t *hash = clicon_options(h);
int i;
char **keys;
void *val;
size_t klen;
size_t vlen;
if (hash == NULL)
return;
keys = hash_keys(hash, &klen);
if (keys == NULL)
return;
for(i = 0; i < klen; i++) {
val = hash_value(hash, keys[i], &vlen);
if (vlen){
if (((char*)val)[vlen-1]=='\0') /* assume string */
clicon_debug(dbglevel, "%s =\t \"%s\"", keys[i], (char*)val);
else
clicon_debug(dbglevel, "%s =\t 0x%p , length %zu", keys[i], val, vlen);
}
else
clicon_debug(dbglevel, "%s = NULL", keys[i]);
}
free(keys);
}
/*! Read filename and set values to global options registry
*/
static int
clicon_option_readfile(clicon_hash_t *copt, const char *filename)
{
struct stat st;
char opt[1024], val[1024];
char line[1024], *cp;
FILE *f;
int retval = -1;
if (filename == NULL || !strlen(filename)){
clicon_err(OE_UNIX, 0, "Not specified");
return -1;
}
if (stat(filename, &st) < 0){
clicon_err(OE_UNIX, errno, "%s", filename);
return -1;
}
if (!S_ISREG(st.st_mode)){
clicon_err(OE_UNIX, 0, "%s is not a regular file", filename);
return -1;
}
if ((f = fopen(filename, "r")) == NULL) {
clicon_err(OE_UNIX, errno, "configure file: %s", filename);
return -1;
}
clicon_debug(2, "Reading config file %s", __FUNCTION__, filename);
while (fgets(line, sizeof(line), f)) {
if ((cp = strchr(line, '\n')) != NULL) /* strip last \n */
*cp = '\0';
/* Trim out comments, strip whitespace, and remove CR */
if ((cp = strchr(line, '#')) != NULL)
memcpy(cp, "\n", 2);
if (sscanf(line, "%s %s", opt, val) < 2)
continue;
if ((hash_add(copt,
opt,
val,
strlen(val)+1)) == NULL)
goto catch;
}
retval = 0;
catch:
fclose(f);
return retval;
}
/*! Set default values of some options that may not appear in config-file
*/
static int
clicon_option_default(clicon_hash_t *copt)
{
char *val;
int retval = 0;
if (!hash_lookup(copt, "CLICON_YANG_MODULE_MAIN")){
if (hash_add(copt, "CLICON_YANG_MODULE_MAIN", "clicon", strlen("clicon")+1) < 0)
goto catch;
}
if (!hash_lookup(copt, "CLICON_SOCK_GROUP")){
val = CLICON_SOCK_GROUP;
if (hash_add(copt, "CLICON_SOCK_GROUP", val, strlen(val)+1) < 0)
goto catch;
}
if (!hash_lookup(copt, "CLICON_CLI_MODE")){
if (hash_add(copt, "CLICON_CLI_MODE", "base", strlen("base")+1) < 0)
goto catch;
}
if (!hash_lookup(copt, "CLICON_MASTER_PLUGIN")){
val = CLICON_MASTER_PLUGIN;
if (hash_add(copt, "CLICON_MASTER_PLUGIN", val, strlen(val)+1) < 0)
goto catch;
}
if (!hash_lookup(copt, "CLICON_CLI_GENMODEL")){
if (hash_add(copt, "CLICON_CLI_GENMODEL", "1", strlen("1")+1) < 0)
goto catch;
}
if (!hash_lookup(copt, "CLICON_CLI_GENMODEL_TYPE")){
if (hash_add(copt, "CLICON_CLI_GENMODEL_TYPE", "VARS", strlen("VARS")+1) < 0)
goto catch;
}
if (!hash_lookup(copt, "CLICON_AUTOCOMMIT")){
if (hash_add(copt, "CLICON_AUTOCOMMIT", "0", strlen("0")+1) < 0)
goto catch;
}
if (!hash_lookup(copt, "CLICON_COMMIT_ORDER")){
if (hash_add(copt, "CLICON_COMMIT_ORDER", "0", strlen("0")+1) < 0)
goto catch;
}
/* Legacy is 1 but default should really be 0. New apps should use 0 */
if (!hash_lookup(copt, "CLICON_CLI_VARONLY")){
if (hash_add(copt, "CLICON_CLI_VARONLY", "1", strlen("1")+1) < 0)
goto catch;
}
if (!hash_lookup(copt, "CLICON_CLI_GENMODEL_COMPLETION")){
if (hash_add(copt, "CLICON_CLI_GENMODEL_COMPLETION", "0", strlen("0")+1) < 0)
goto catch;
}
retval = 0;
catch:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Check that options are set
*/
static int
clicon_option_sanity(clicon_hash_t *copt)
{
int retval = -1;
if (!hash_lookup(copt, "CLICON_CLI_DIR")){
clicon_err(OE_UNIX, 0, "CLICON_CLI_DIR not defined in config file");
goto done;
}
if (!hash_lookup(copt, "CLICON_CLISPEC_DIR")){
clicon_err(OE_UNIX, 0, "CLICON_CLISPEC_DIR not defined in config file");
goto done;
}
if (!hash_lookup(copt, "CLICON_BACKEND_DIR")){
clicon_err(OE_UNIX, 0, "CLICON_BACKEND_PIDFILE not defined in config file");
goto done;
}
if (!hash_lookup(copt, "CLICON_NETCONF_DIR")){
clicon_err(OE_UNIX, 0, "CLICON_NETCONF_DIR not defined in config file");
goto done;
}
if (!hash_lookup(copt, "CLICON_RUNNING_DB")){
clicon_err(OE_UNIX, 0, "CLICON_RUNNING_DB not defined in config file");
goto done;
}
if (!hash_lookup(copt, "CLICON_CANDIDATE_DB")){
clicon_err(OE_UNIX, 0, "CLICON_CANDIDATE_DB not defined in config file");
goto done;
}
if (!hash_lookup(copt, "CLICON_YANG_DIR")){
clicon_err(OE_UNIX, 0, "CLICON_YANG_DIR not defined in config file");
goto done;
}
if (!hash_lookup(copt, "CLICON_ARCHIVE_DIR")){
clicon_err(OE_UNIX, 0, "CLICON_ARCHIVE_DIR not defined in config file");
goto done;
}
if (!hash_lookup(copt, "CLICON_STARTUP_CONFIG")){
clicon_err(OE_UNIX, 0, "CLICON_STARTUP_CONFIG not defined in config file");
goto done;
}
if (!hash_lookup(copt, "CLICON_SOCK")){
clicon_err(OE_UNIX, 0, "CLICON_SOCK not defined in config file");
goto done;
}
if (!hash_lookup(copt, "CLICON_BACKEND_PIDFILE")){
clicon_err(OE_UNIX, 0, "CLICON_BACKEND_PIDFILE not defined in config file");
goto done;
}
retval = 0;
done:
return retval;
}
/*! Initialize option values
*
* Set default options, Read config-file, Check that all values are set.
* @param[in] h clicon handle
*/
int
clicon_options_main(clicon_handle h)
{
int retval = -1;
char *configfile;
clicon_hash_t *copt = clicon_options(h);
/*
* Set configure file if not set by command-line above
*/
if (!hash_lookup(copt, "CLICON_CONFIGFILE")){
clicon_err(OE_CFG, 0, "CLICON_CONFIGFILE (-f) not set");
goto done;
}
configfile = hash_value(copt, "CLICON_CONFIGFILE", NULL);
clicon_debug(1, "CLICON_CONFIGFILE=%s", configfile);
/* Set default options */
if (clicon_option_default(copt) < 0) /* init registry from file */
goto done;
/* Read configfile */
if (clicon_option_readfile(copt, configfile) < 0)
goto done;
if (clicon_option_sanity(copt) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Check if a clicon option has a value
*/
int
clicon_option_exists(clicon_handle h, const char *name)
{
clicon_hash_t *copt = clicon_options(h);
return (hash_lookup(copt, (char*)name) != NULL);
}
/*! Get a single string option string via handle
*
* @param[in] h clicon_handle
* @param[in] name option name
* @retval NULL If option not found, or value of option is NULL
* @retval string value of option if found
* clicon options should be strings.
* To differentiate the two reasons why NULL may be returned, use function
* clicon_option_exists() before the call
*/
char *
clicon_option_str(clicon_handle h, const char *name)
{
clicon_hash_t *copt = clicon_options(h);
if (hash_lookup(copt, (char*)name) == NULL)
return NULL;
return hash_value(copt, (char*)name, NULL);
}
/*! Set a single string option via handle
*/
int
clicon_option_str_set(clicon_handle h, const char *name, char *val)
{
clicon_hash_t *copt = clicon_options(h);
return hash_add(copt, (char*)name, val, strlen(val)+1)==NULL?-1:0;
}
/*! Get options as integer but stored as string
* @param h clicon handle
* @param name name of option
* @retval int An integer as aresult of atoi
* @retval -1 If option does not exist
* @code
* if (clicon_option_exists(h, "X")
* return clicon_option_int(h, "X");
* else
* return 0;
* @endcode
* Note that -1 can be both error and value.
* This means that it should be used together with clicon_option_exists() and
* supply a defualt value as shown in the example.
*/
int
clicon_option_int(clicon_handle h, const char *name)
{
char *s;
if ((s = clicon_option_str(h, name)) == NULL)
return -1;
return atoi(s);
}
/*! set option given as int.
*/
int
clicon_option_int_set(clicon_handle h, const char *name, int val)
{
char s[64];
if (snprintf(s, sizeof(s)-1, "%u", val) < 0)
return -1;
return clicon_option_str_set(h, name, s);
}
/*! delete option
*/
int
clicon_option_del(clicon_handle h, const char *name)
{
clicon_hash_t *copt = clicon_options(h);
return hash_del(copt, (char*)name);
}
/*-----------------------------------------------------------------
* Specific option access functions.
* These should be commonly accessible for all users of clicon lib
*-----------------------------------------------------------------*/
char *
clicon_configfile(clicon_handle h)
{
return clicon_option_str(h, "CLICON_CONFIGFILE");
}
/*! YANG database specification directory */
char *
clicon_yang_dir(clicon_handle h)
{
return clicon_option_str(h, "CLICON_YANG_DIR");
}
/*! YANG main module */
char *
clicon_yang_module_main(clicon_handle h)
{
return clicon_option_str(h, "CLICON_YANG_MODULE_MAIN");
}
/*! YANG revision */
char *
clicon_yang_module_revision(clicon_handle h)
{
return clicon_option_str(h, "CLICON_YANG_MODULE_REVISION");
}
/* candidate database: get name */
char *
clicon_candidate_db(clicon_handle h)
{
return clicon_option_str(h, "CLICON_CANDIDATE_DB");
}
/* running database: get name */
char *
clicon_running_db(clicon_handle h)
{
return clicon_option_str(h, "CLICON_RUNNING_DB");
}
char *
clicon_backend_dir(clicon_handle h)
{
return clicon_option_str(h, "CLICON_BACKEND_DIR");
}
/* contains .so files CLICON_CLI_DIR */
char *
clicon_cli_dir(clicon_handle h)
{
return clicon_option_str(h, "CLICON_CLI_DIR");
}
/* contains .cli files - CLICON_CLISPEC_DIR */
char *
clicon_clispec_dir(clicon_handle h)
{
return clicon_option_str(h, "CLICON_CLISPEC_DIR");
}
char *
clicon_netconf_dir(clicon_handle h)
{
return clicon_option_str(h, "CLICON_NETCONF_DIR");
}
char *
clicon_archive_dir(clicon_handle h)
{
return clicon_option_str(h, "CLICON_ARCHIVE_DIR");
}
char *
clicon_startup_config(clicon_handle h)
{
return clicon_option_str(h, "CLICON_STARTUP_CONFIG");
}
/* get family of backend socket: AF_UNIX, AF_INET or AF_INET6 */
int
clicon_sock_family(clicon_handle h)
{
char *s;
if ((s = clicon_option_str(h, "CLICON_SOCK_FAMILY")) == NULL)
return AF_UNIX;
else if (strcmp(s, "IPv4")==0)
return AF_INET;
else if (strcmp(s, "IPv6")==0)
return AF_INET6;
else
return AF_UNIX; /* default */
}
/* get information about socket: unix domain filepath, or addr:path */
char *
clicon_sock(clicon_handle h)
{
return clicon_option_str(h, "CLICON_SOCK");
}
/* get port for backend socket in case of AF_INET or AF_INET6 */
int
clicon_sock_port(clicon_handle h)
{
char *s;
if ((s = clicon_option_str(h, "CLICON_SOCK_PORT")) == NULL)
return -1;
return atoi(s);
}
char *
clicon_backend_pidfile(clicon_handle h)
{
return clicon_option_str(h, "CLICON_BACKEND_PIDFILE");
}
char *
clicon_sock_group(clicon_handle h)
{
return clicon_option_str(h, "CLICON_SOCK_GROUP");
}
char *
clicon_master_plugin(clicon_handle h)
{
return clicon_option_str(h, "CLICON_MASTER_PLUGIN");
}
/* return initial clicon cli mode */
char *
clicon_cli_mode(clicon_handle h)
{
return clicon_option_str(h, "CLICON_CLI_MODE");
}
/*! Whether to generate CLIgen syntax from datamodel or not (0 or 1)
* Must be used with a previous clicon_option_exists().
*/
int
clicon_cli_genmodel(clicon_handle h)
{
char const *opt = "CLICON_CLI_GENMODEL";
if (clicon_option_exists(h, opt))
return clicon_option_int(h, opt);
else
return 0;
}
/* How to generate and show CLI syntax: VARS|ALL */
enum genmodel_type
clicon_cli_genmodel_type(clicon_handle h)
{
char *s;
enum genmodel_type gt = GT_ERR;
if (!clicon_option_exists(h, "CLICON_CLI_GENMODEL_TYPE")){
gt = GT_VARS;
goto done;
}
s = clicon_option_str(h, "CLICON_CLI_GENMODEL_TYPE");
if (strcmp(s, "NONE")==0)
gt = GT_NONE;
else
if (strcmp(s, "VARS")==0)
gt = GT_VARS;
else
if (strcmp(s, "ALL")==0)
gt = GT_ALL;
done:
return gt;
}
/* eg -q option, dont print notifications on stdout */
char *
clicon_quiet_mode(clicon_handle h)
{
return clicon_option_str(h, "CLICON_QUIET");
}
int
clicon_autocommit(clicon_handle h)
{
char const *opt = "CLICON_AUTOCOMMIT";
if (clicon_option_exists(h, opt))
return clicon_option_int(h, opt);
else
return 0;
}
int
clicon_autocommit_set(clicon_handle h, int val)
{
return clicon_option_int_set(h, "CLICON_AUTOCOMMIT", val);
}
/*! Get backend callback order.
* 0: all callbacks in (rising) priority order
* 1: first delete operations in declining prio order; then add/change in prio order
* 2: like (1) but CHANGE is replaced by (DEL;ADD)
*/
int
clicon_commit_order(clicon_handle h)
{
char const *opt = "CLICON_COMMIT_ORDER";
if (clicon_option_exists(h, opt))
return clicon_option_int(h, opt);
else
return 0;
}
/*! Dont include keys in cvec in cli vars callbacks
*/
int
clicon_cli_varonly(clicon_handle h)
{
char const *opt = "CLICON_CLI_VARONLY";
if (clicon_option_exists(h, opt))
return clicon_option_int(h, opt);
else
return 0;
}
int
clicon_cli_varonly_set(clicon_handle h, int val)
{
return clicon_option_int_set(h, "CLICON_CLI_VARONLY", val);
}
int
clicon_cli_genmodel_completion(clicon_handle h)
{
char const *opt = "CLICON_CLI_GENMODEL_COMPLETION";
if (clicon_option_exists(h, opt))
return clicon_option_int(h, opt);
else
return 0;
}
/*
* Get dbspec (YANG variant)
* Must use hash functions directly since they are not strings.
*/
yang_spec *
clicon_dbspec_yang(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = hash_value(cdat, "dbspec_yang", &len)) != NULL)
return *(yang_spec **)p;
return NULL;
}
/*
* Set dbspec (YANG variant)
* ys must be a malloced pointer
*/
int
clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to ys that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (hash_add(cdat, "dbspec_yang", &ys, sizeof(ys)) == NULL)
return -1;
return 0;
}
/*
* Get dbspec name as read from spec. Can be used in CLI '@' syntax.
* XXX: this we muśt change,...
*/
char *
clicon_dbspec_name(clicon_handle h)
{
if (!clicon_option_exists(h, "dbspec_name"))
return NULL;
return clicon_option_str(h, "dbspec_name");
}
/*
* Set dbspec name as read from spec. Can be used in CLI '@' syntax.
*/
int
clicon_dbspec_name_set(clicon_handle h, char *name)
{
return clicon_option_str_set(h, "dbspec_name", name);
}

73
lib/src/clicon_plugin.c Normal file
View file

@ -0,0 +1,73 @@
/*
*
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 <stdlib.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#include "clicon_err.h"
#include "clicon_queue.h"
#include "clicon_hash.h"
#include "clicon_handle.h"
#include "clicon_plugin.h"
static find_plugin_t *
clicon_find_plugin(clicon_handle h)
{
void *p;
find_plugin_t *fp = NULL;
clicon_hash_t *data = clicon_data(h);
if ((p = hash_value(data, "CLICON_FIND_PLUGIN", NULL)) != NULL)
memcpy(&fp, p, sizeof(fp));
return fp;
}
/*! Return a function pointer based on name of plugin and function.
* If plugin is specified, ask daemon registered function to return
* the dlsym handle of the plugin.
*/
void *
clicon_find_func(clicon_handle h, char *plugin, char *func)
{
find_plugin_t *plgget;
void *dlhandle = NULL;
if (plugin) {
/* find clicon_plugin_get() in global namespace */
if ((plgget = clicon_find_plugin(h)) == NULL) {
clicon_err(OE_UNIX, errno, "Specified plugin not supported");
return NULL;
}
dlhandle = plgget(h, plugin);
}
return dlsym(dlhandle, func);
}

354
lib/src/clicon_proc.c Normal file
View file

@ -0,0 +1,354 @@
/*
*
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"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <grp.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/user.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/resource.h>
/* clicon */
#include "clicon_err.h"
#include "clicon_log.h"
#include "clicon_sig.h"
#include "clicon_string.h"
#include "clicon_queue.h"
#include "clicon_chunk.h"
#include "clicon_proc.h"
/*
* Macros
*/
#define signal_set_mask(set) sigprocmask(SIG_SETMASK, (set), NULL)
#define signal_get_mask(set) sigprocmask (0, NULL, (set))
/*
* Child process ID
* XXX Really shouldn't be a global variable
*/
static int _clicon_proc_child = 0;
/*
* Make sure child is killed by ctrl-C
*/
static void
clicon_proc_sigint(int sig)
{
if (_clicon_proc_child > 0)
kill (_clicon_proc_child, SIGINT);
}
/*
* Fork a child process, setup a pipe between parent and child, allowing
* parent to read the output of the child. If 'doerr' is non-zero, stderr
* will be directed to the pipe as well. The pipe for the parent to write
* to the child is closed and cannot be used.
*
* When child process is done with the pipe setup, execute the specified
* command, execv(argv[0], argv).
*
* When parent is done with the pipe setup it will read output from the child
* until eof. The read output will be sent to the specified output callback,
* 'outcb' function.
*
* Return number of matches (processes affected). -1 on error.
*/
int
clicon_proc_run (char *cmd, void (outcb)(char *), int doerr)
{
char
**argv,
buf[512];
int
outfd[2] = { -1, -1 };
int
n,
argc,
status,
retval = -1;
pid_t
child;
sigfn_t oldhandler = NULL;
sigset_t oset;
argv = clicon_sepsplit (cmd, " \t", &argc, __FUNCTION__);
if (!argv)
return -1;
if (pipe (outfd) == -1)
goto done;
signal_get_mask(&oset);
set_signal(SIGINT, clicon_proc_sigint, &oldhandler);
if ((child = fork ()) < 0) {
retval = -1;
goto done;
}
if (child == 0) { /* Child */
/* Unblock all signals except TSTP */
clicon_signal_unblock (0);
signal (SIGTSTP, SIG_IGN);
close (outfd[0]); /* Close unused read ends */
outfd[0] = -1;
/* Divert stdout and stderr to pipes */
dup2 (outfd[1], STDOUT_FILENO);
if (doerr)
dup2 (outfd[1], STDERR_FILENO);
execvp (argv[0], argv);
perror("execvp");
_exit(-1);
}
/* Parent */
/* Close unused write ends */
close (outfd[1]);
outfd[1] = -1;
/* Read from pipe */
while ((n = read (outfd[0], buf, sizeof (buf)-1)) != 0) {
if (n < 0) {
if (errno == EINTR)
continue;
break;
}
buf[n] = '\0';
/* Pass read data to callback function is defined */
if (outcb)
outcb (buf);
}
/* Wait for child to finish */
if(waitpid (child, &status, 0) == child)
retval = WEXITSTATUS(status);
else
retval = -1;
done:
/* Clean up all pipes */
if (outfd[0] != -1)
close (outfd[0]);
if (outfd[1] != -1)
close (outfd[1]);
/* Restore sigmask and fn */
signal_set_mask (&oset);
set_signal(SIGINT, oldhandler, NULL);
unchunk_group (__FUNCTION__);
return retval;
}
/*
* Spawm command and report exit status
*/
int
clicon_proc_daemon (char *cmd)
{
char
**argv;
int
i,
argc,
retval = -1,
status, status2;
pid_t
child,
pid;
struct rlimit
rlim;
argv = clicon_sepsplit (cmd, " \t", &argc, NULL);
if (!argv)
return -1;
if ((child = fork ()) < 0) {
clicon_err(OE_UNIX, errno, "fork");
goto done;
}
if (child == 0) { /* Child */
clicon_signal_unblock (0);
if ((pid = fork ()) < 0) {
clicon_err(OE_UNIX, errno, "fork");
return -1;
_exit(1);
}
if (pid == 0) { /* Grandchild, create new session */
setsid();
if (chdir("/") < 0){
clicon_err(OE_UNIX, errno, "chdirq");
_exit(1);
}
/* Close open descriptors */
if ( ! getrlimit (RLIMIT_NOFILE, &rlim))
for (i = 0; i < rlim.rlim_cur; i++)
close(i);
if (execv (argv[0], argv) < 0) {
clicon_err(OE_UNIX, errno, "execv");
_exit(1);
}
/* Not reached */
}
waitpid (pid, &status2, 0);
_exit(status2);
}
if (waitpid (child, &status, 0) > 0)
retval = 0;
done:
unchunk_group(__FUNCTION__);
return (retval);
}
#ifdef moved_to_osr
#ifdef linux
/*
* Send 'sig' (if not 0) to all processes matching 'name'.
* Return the number of matches.
*/
int
clicon_proc_killbyname (const char *name, int sig)
{
/* XXXX FIXME. Should scan /proc/<pid>/status */
char buf[512];
snprintf (buf, sizeof (buf)-1, "pkill -%d %s", sig, name);
return clicon_proc_run (buf, NULL, 0);
}
#endif /* Linux */
#ifdef BSD
/*
* Send 'sig' (if not 0) to all processes matching 'name'.
* Return the number of matches.
*/
int
clicon_proc_killbyname (const char *name, int sig)
{
int
i,
nproc,
nmatch,
mib[3];
size_t
size;
struct proc
*p;
struct kinfo_proc
*kp = NULL;
mib[0] = CTL_KERN;
mib[1] = KERN_NPROCS; /* KERN_MACPROC, KERN_PROC */
size = sizeof (nproc);
if (sysctl(mib, 2, &nproc, &size, NULL, 0) < 0)
return -1;
size = nproc * sizeof(struct kinfo_proc);
kp = chunk(size * sizeof(char), "bsdkill");
if (kp == NULL)
goto error;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_ALL;
if (sysctl(mib, 3, kp, &size, NULL, 0) < 0)
goto error;
nproc = size / sizeof(struct kinfo_proc);
for (nmatch = i = 0; i < nproc; i++) {
p = (struct proc *)&kp[i].kp_proc;
if (!strcmp (name, p->p_comm)) {
nmatch++;
kill (p->p_pid, sig);
}
}
unchunk (kp);
return nmatch;
error:
if (kp)
unchunk (kp);
return -1;
}
#endif /* BSD */
#endif
/*! Translate group name to gid. Return -1 if error or not found.
*/
int
group_name2gid(char *name, gid_t *gid)
{
char buf[1024];
struct group g0;
struct group *gr = &g0;
struct group *gtmp;
gr = &g0;
/* This leaks memory in ubuntu */
if (getgrnam_r(name, gr, buf, sizeof(buf), &gtmp) < 0){
clicon_err(OE_UNIX, errno, "%s: getgrnam_r(%s): %s",
__FUNCTION__, name, strerror(errno));
return -1;
}
if (gtmp == NULL){
clicon_err(OE_UNIX, 0, "%s: No such group: %s", __FUNCTION__, name);
fprintf(stderr, "No such group %s\n", name);
return -1;
}
if (gid)
*gid = gr->gr_gid;
return 0;
}

515
lib/src/clicon_proto.c Normal file
View file

@ -0,0 +1,515 @@
/*
*
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/>.
*
* Protocol to communicate between clients (eg clicon_cli, clicon_netconf)
* and server (clicon_backend)
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <syslog.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <arpa/inet.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clicon_err.h"
#include "clicon_log.h"
#include "clicon_queue.h"
#include "clicon_chunk.h"
#include "clicon_sig.h"
#include "clicon_proto.h"
#include "clicon_proto_encode.h"
static int _atomicio_sig = 0;
struct map_type2str{
enum clicon_msg_type mt_type;
char *mt_str; /* string as in 4.2.4 in RFC 6020 */
};
/* Mapping between yang keyword string <--> clicon constants */
static const struct map_type2str msgmap[] = {
{CLICON_MSG_COMMIT, "commit"},
{CLICON_MSG_VALIDATE, "validate"},
{CLICON_MSG_CHANGE, "change"},
{CLICON_MSG_SAVE, "save"},
{CLICON_MSG_LOAD, "load"},
{CLICON_MSG_COPY, "copy"},
{CLICON_MSG_RM, "rm"},
{CLICON_MSG_INITDB, "initdb"},
{CLICON_MSG_LOCK, "lock"},
{CLICON_MSG_UNLOCK, "unlock"},
{CLICON_MSG_KILL, "kill"},
{CLICON_MSG_DEBUG, "debug"},
{CLICON_MSG_CALL, "call"},
{CLICON_MSG_SUBSCRIPTION, "subscription"},
{CLICON_MSG_OK, "ok"},
{CLICON_MSG_NOTIFY, "notify"},
{CLICON_MSG_ERR, "err"},
{-1, NULL},
};
static char *
msg_type2str(enum clicon_msg_type type)
{
const struct map_type2str *mt;
for (mt = &msgmap[0]; mt->mt_str; mt++)
if (mt->mt_type == type)
return mt->mt_str;
return NULL;
}
/*! Open local connection using unix domain sockets
*/
int
clicon_connect_unix(char *sockpath)
{
struct sockaddr_un addr;
int retval = -1;
int s;
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
clicon_err(OE_CFG, errno, "socket");
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path)-1);
clicon_debug(2, "%s: connecting to %s", __FUNCTION__, addr.sun_path);
if (connect(s, (struct sockaddr *)&addr, SUN_LEN(&addr)) < 0){
if (errno == EACCES)
clicon_err(OE_CFG, errno, "connecting unix socket: %s.\n"
"Client should be member of group $CLICON_SOCK_GROUP: ",
sockpath);
else
clicon_err(OE_CFG, errno, "connecting unix socket: %s", sockpath);
close(s);
goto done;
}
retval = s;
done:
return retval;
}
static void
atomicio_sig_handler(int arg)
{
_atomicio_sig++;
}
/*! Ensure all of data on socket comes through. fn is either read or write
*/
static ssize_t
atomicio(ssize_t (*fn) (int, void *, size_t), int fd, void *_s, size_t n)
{
char *s = _s;
ssize_t res, pos = 0;
while (n > pos) {
_atomicio_sig = 0;
res = (fn)(fd, s + pos, n - pos);
switch (res) {
case -1:
if (errno == EINTR){
if (!_atomicio_sig)
continue;
}
else
if (errno == EAGAIN)
continue;
case 0: /* fall thru */
return (res);
default:
pos += res;
}
}
return (pos);
}
static int
msg_dump(struct clicon_msg *msg)
{
int i;
char buf[9*8];
char buf2[9*8];
memset(buf2, 0, sizeof(buf2));
snprintf(buf2, sizeof(buf2), "%s:", __FUNCTION__);
for (i=0; i<ntohs(msg->op_len); i++){
snprintf(buf, sizeof(buf), "%s%02x", buf2, ((char*)msg)[i]&0xff);
if ((i+1)%32==0){
clicon_debug(2, buf);
snprintf(buf, sizeof(buf), "%s:", __FUNCTION__);
}
else
if ((i+1)%4==0)
snprintf(buf, sizeof(buf), "%s ", buf2);
strncpy(buf2, buf, sizeof(buf2));
}
if (i%32)
clicon_debug(2, buf);
return 0;
}
int
clicon_msg_send(int s, struct clicon_msg *msg)
{
int retval = -1;
clicon_debug(2, "%s: send msg seq=%d len=%d",
__FUNCTION__, ntohs(msg->op_type), ntohs(msg->op_len));
if (debug > 2)
msg_dump(msg);
if (atomicio((ssize_t (*)(int, void *, size_t))write,
s, msg, ntohs(msg->op_len)) < 0){
clicon_err(OE_CFG, errno, "%s", __FUNCTION__);
goto done;
}
retval = 0;
done:
return retval;
}
/*! Receive a CLICON message on a UNIX domain socket
*
* XXX: timeout? and signals?
* There is rudimentary code for turning on signals and handling them
* so that they can be interrupted by ^C. But the problem is that this
* is a library routine and such things should be set up in the cli
* application for example: a daemon calling this function will want another
* behaviour.
* Now, ^C will interrupt the whole process, and this may not be what you want.
*
* @param[in] s UNIX domain socket to communicate with backend
* @param[out] msg CLICON msg data reply structure. allocated using CLICON chunks,
* freed by caller with unchunk*(...,label)
* @param[out] eof Set if eof encountered
* @param[in] label Label used in chunk allocation and deallocation.
* Note: caller must ensure that s is closed if eof is set after call.
*/
int
clicon_msg_rcv(int s,
struct clicon_msg **msg,
int *eof,
const char *label)
{
int retval = -1;
struct clicon_msg hdr;
int hlen;
int len2;
sigfn_t oldhandler;
uint16_t mlen;
*eof = 0;
if (0)
set_signal(SIGINT, atomicio_sig_handler, &oldhandler);
if ((hlen = atomicio(read, s, &hdr, sizeof(hdr))) < 0){
clicon_err(OE_CFG, errno, "%s", __FUNCTION__);
goto done;
}
if (hlen == 0){
retval = 0;
*eof = 1;
goto done;
}
if (hlen != sizeof(hdr)){
clicon_err(OE_CFG, errno, "%s: header too short (%d)", __FUNCTION__, hlen);
goto done;
}
mlen = ntohs(hdr.op_len);
clicon_debug(2, "%s: rcv msg seq=%d, len=%d",
__FUNCTION__, ntohs(hdr.op_type), mlen);
if ((*msg = (struct clicon_msg *)chunk(mlen, label)) == NULL){
clicon_err(OE_CFG, errno, "%s: chunk", __FUNCTION__);
goto done;
}
memcpy(*msg, &hdr, hlen);
if ((len2 = read(s, (*msg)->op_body, mlen - sizeof(hdr))) < 0){
clicon_err(OE_CFG, errno, "%s: read", __FUNCTION__);
goto done;
}
if (len2 != mlen - sizeof(hdr)){
clicon_err(OE_CFG, errno, "%s: body too short", __FUNCTION__);
goto done;
}
if (debug > 1)
msg_dump(*msg);
retval = 0;
done:
if (0)
set_signal(SIGINT, oldhandler, NULL);
return retval;
}
/*! Connect to server, send an clicon_msg message and wait for result.
* Compared to clicon_rpc, this is a one-shot rpc: open, send, get reply and close.
* NOTE: this is dependent on unix domain
*/
int
clicon_rpc_connect_unix(struct clicon_msg *msg,
char *sockpath,
char **data,
uint16_t *datalen,
int *sock0,
const char *label)
{
int retval = -1;
int s = -1;
struct stat sb;
clicon_debug(1, "Send %s msg on %s",
msg_type2str(ntohs(msg->op_type)), sockpath);
/* special error handling to get understandable messages (otherwise ENOENT) */
if (stat(sockpath, &sb) < 0){
clicon_err(OE_PROTO, errno, "%s: config daemon not running?", sockpath);
goto done;
}
if (!S_ISSOCK(sb.st_mode)){
clicon_err(OE_PROTO, EIO, "%s: Not unix socket", sockpath);
goto done;
}
if ((s = clicon_connect_unix(sockpath)) < 0)
goto done;
if (clicon_rpc(s, msg, data, datalen, label) < 0)
goto done;
if (sock0 != NULL)
*sock0 = s;
retval = 0;
done:
if (sock0 == NULL && s >= 0)
close(s);
return retval;
}
/*! Connect to server, send an clicon_msg message and wait for result using an inet socket
* Compared to clicon_rpc, this is a one-shot rpc: open, send, get reply and close.
*/
int
clicon_rpc_connect_inet(struct clicon_msg *msg,
char *dst,
uint16_t port,
char **data,
uint16_t *datalen,
int *sock0,
const char *label)
{
int retval = -1;
int s = -1;
struct sockaddr_in addr;
clicon_debug(1, "Send %s msg to %s:%hu",
msg_type2str(ntohs(msg->op_type)), dst, port);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (inet_pton(addr.sin_family, dst, &addr.sin_addr) != 1)
goto done; /* Could check getaddrinfo */
/* special error handling to get understandable messages (otherwise ENOENT) */
if ((s = socket(addr.sin_family, SOCK_STREAM, 0)) < 0) {
clicon_err(OE_CFG, errno, "socket");
return -1;
}
if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) < 0){
clicon_err(OE_CFG, errno, "connecting socket inet4");
close(s);
goto done;
}
if (clicon_rpc(s, msg, data, datalen, label) < 0)
goto done;
if (sock0 != NULL)
*sock0 = s;
retval = 0;
done:
if (sock0 == NULL && s >= 0)
close(s);
return retval;
}
/*! Send a clicon_msg message and wait for result.
*
* TBD: timeout, interrupt?
* retval may be -1 and
* errno set to ENOTCONN which means that socket is now closed probably
* due to remote peer disconnecting. The caller may have to do something,...
*
* @param[in] s Socket to communicate with backend
* @param[in] msg CLICON msg data structure. It has fixed header and variable body.
* @param[out] data Returned data as byte-strin exclusing header.
* Deallocate w unchunk...(..., label)
* @param[out] datalen Length of returned data
* @param[in] label Label used in chunk allocation.
*/
int
clicon_rpc(int s,
struct clicon_msg *msg,
char **data,
uint16_t *datalen,
const char *label)
{
int retval = -1;
struct clicon_msg *reply;
int eof;
uint32_t err;
uint32_t suberr;
char *reason;
enum clicon_msg_type type;
if (clicon_msg_send(s, msg) < 0)
goto done;
if (clicon_msg_rcv(s, &reply, &eof, label) < 0)
goto done;
if (eof){
clicon_err(OE_PROTO, ESHUTDOWN, "%s: Socket unexpected close", __FUNCTION__);
close(s);
errno = ESHUTDOWN;
goto done;
}
type = ntohs(reply->op_type);
switch (type){
case CLICON_MSG_OK:
if (data != NULL) {
*data = reply->op_body;
*datalen = ntohs(reply->op_len) - sizeof(*reply);
}
break;
case CLICON_MSG_ERR:
if (clicon_msg_err_decode(reply, &err, &suberr, &reason, label) < 0)
goto done;
clicon_err(err, suberr, "%s", reason);
goto done;
break;
default:
clicon_err(OE_PROTO, 0, "%s: unexpected reply: %d",
__FUNCTION__, type);
goto done;
break;
}
retval = 0;
done:
return retval;
}
int
send_msg_reply(int s,
uint16_t type,
char *data,
uint16_t datalen)
{
int retval = -1;
struct clicon_msg *reply;
uint16_t len;
len = sizeof(*reply) + datalen;
if ((reply = (struct clicon_msg *)chunk(len, __FUNCTION__)) == NULL)
goto done;
memset(reply, 0, len);
reply->op_type = htons(type);
reply->op_len = htons(len);
if (datalen > 0)
memcpy(reply->op_body, data, datalen);
if (clicon_msg_send(s, reply) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
int
send_msg_ok(int s)
{
return send_msg_reply(s, CLICON_MSG_OK, NULL, 0);
}
int
send_msg_notify(int s, int level, char *event)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_notify_encode(level, event, __FUNCTION__)) == NULL)
goto done;
if (clicon_msg_send(s, msg) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
int
send_msg_err(int s, int err, int suberr, char *format, ...)
{
va_list args;
char *reason;
int len;
int retval = -1;
struct clicon_msg *msg;
va_start(args, format);
len = vsnprintf(NULL, 0, format, args) + 1;
va_end(args);
if ((reason = (char *)chunk(len, __FUNCTION__)) == NULL)
return -1;
memset(reason, 0, len);
va_start(args, format);
vsnprintf(reason, len, format, args);
va_end(args);
if ((msg=clicon_msg_err_encode(clicon_errno, clicon_suberrno,
reason, __FUNCTION__)) == NULL)
goto done;
if (clicon_msg_send(s, msg) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}

View file

@ -0,0 +1,524 @@
/*
*
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/>.
*
* Client-side functions for clicon_proto protocol
* Historically this code was part of the clicon_cli application. But
* it should (is?) be general enough to be used by other applications.
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/socket.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clicon_queue.h"
#include "clicon_chunk.h"
#include "clicon_hash.h"
#include "clicon_handle.h"
#include "clicon_yang.h"
#include "clicon_options.h"
#include "clicon_proto.h"
#include "clicon_err.h"
#include "clicon_xml.h"
#include "clicon_proto_encode.h"
#include "clicon_proto_client.h"
/*! Internal rpc function
* @param[in] h CLICON handle
* @param[in] msg Encoded message
* @param[out] ret Return value from backend server (reply)
* @param[out] retlen Length of return value
* @param[inout] sock0 If pointer exists, do not close socket to backend on success
* and return it here. For keeping a notify socket open
* @param[in] label Chunk label for deallocating return values
* Deallocate with unchunk_group(label)
* Note: sock0 is if connection should be persistent, like a notification/subscribe api
*/
static int
clicon_rpc_msg(clicon_handle h,
struct clicon_msg *msg,
char **ret,
uint16_t *retlen,
int *sock0,
const char *label)
{
int retval = -1;
char *sock;
int port;
if ((sock = clicon_sock(h)) == NULL){
clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
goto done;
}
/* What to do if inet socket? */
switch (clicon_sock_family(h)){
case AF_UNIX:
if (clicon_rpc_connect_unix(msg, sock, ret, retlen, sock0, label) < 0){
#if 0
if (errno == ESHUTDOWN)
/* Maybe could reconnect on a higher layer, but lets fail
loud and proud */
cli_set_exiting(1);
#endif
goto done;
}
break;
case AF_INET:
if ((port = clicon_sock_port(h)) < 0){
clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
goto done;
}
if (port < 0){
clicon_err(OE_FATAL, 0, "CLICON_SOCK_PORT not set");
goto done;
}
if (clicon_rpc_connect_inet(msg, sock, port, ret, retlen, sock0, label) < 0)
goto done;
break;
}
retval = 0;
done:
return retval;
}
/*! Commit changes send a commit request to backend daemon
* @param[in] h CLICON handle
* @param[in] running_db Name of database
* @param[in] db Name of database
* @param[in] snapshot Make a snapshot copy of db state
* @param[in] startup Make a copy to startup.
* @retval 0 Copy current->candidate
*/
int
clicon_rpc_commit(clicon_handle h,
char *running_db,
char *db,
int snapshot,
int startup)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_commit_encode(db, running_db, snapshot, startup,
__FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send validate request to backend daemon
* @param[in] h CLICON handle
* @param[in] db Name of database
* @retval 0
*/
int
clicon_rpc_validate(clicon_handle h,
char *db)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_validate_encode(db, __FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send database change request to backend daemon, variant for xmldb
* Same as clicon_proto_change just with a string
* @param[in] h CLICON handle
* @param[in] db Name of database
* @param[in] op Operation on database item: set, delete, (merge?)
* @param[in] key Database key
* @param[in] value value as string
* @retval 0 OK
* @retval -1 Error
*/
int
clicon_rpc_change(clicon_handle h,
char *db,
enum operation_type op,
char *key,
char *val)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg = clicon_msg_change_encode(db,
op,
key,
val,
strlen(val)+1,
__FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send database entries as XML to backend daemon
* Same as clicon_proto_change just with a cvec instead of lvec
* @param[in] h CLICON handle
* @param[in] db Name of database
* @param[in] op Operation on database item: OP_MERGE, OP_REPLACE
* @param[in] xml XML string. Ex: <a>..</a><b>...</b>
* @retval 0 OK
* @retval -1 Error
*/
int
clicon_rpc_xmlput(clicon_handle h,
char *db,
enum operation_type op,
char *xml)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg = clicon_msg_xmlput_encode(db,
(uint32_t)op,
xml,
__FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send database save request to backend daemon
* @param[in] h CLICON handle
* @param[in] db Name of database
* @param[in] snapshot Save to snapshot file
* @param[in] filename Save to file (backend file-system)
*/
int
clicon_rpc_save(clicon_handle h,
char *db,
int snapshot,
char *filename)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_save_encode(db, snapshot, filename,
__FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send database load request to backend daemon
* @param[in] h CLICON handle
* @param[in] replace 0: merge with existing data, 1:replace completely
* @param[in] db Name of database
* @param[in] filename Load from file (backend file-system)
*/
int
clicon_rpc_load(clicon_handle h,
int replace,
char *db,
char *filename)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_load_encode(replace, db, filename,
__FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send a request to backend to copy a file from one location to another
* Note this assumes the backend can access these files and (usually) assumes
* clients and servers have the access to the same filesystem.
* @param[in] h CLICON handle
* @param[in] filename1 src file
* @param[in] filename2 dst file
*/
int
clicon_rpc_copy(clicon_handle h,
char *filename1,
char *filename2)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_copy_encode(filename1, filename2,
__FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send a request to remove a file from backend file system
* @param[in] h CLICON handle
* @param[in] filename File to remove
*/
int
clicon_rpc_rm(clicon_handle h,
char *filename)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_rm_encode(filename, __FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send a database initialization request to backend server
* @param[in] h CLICON handle
* @param[in] db Name of database
*/
int
clicon_rpc_initdb(clicon_handle h,
char *db)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_initdb_encode(db, __FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send a database lock request to backend server
* @param[in] h CLICON handle
* @param[in] db Name of database
*/
int
clicon_rpc_lock(clicon_handle h,
char *db)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_lock_encode(db, __FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send a database unlock request to backend server
* @param[in] h CLICON handle
* @param[in] db Name of database
*/
int
clicon_rpc_unlock(clicon_handle h,
char *db)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_unlock_encode(db, __FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send a kill session request to backend server
* @param[in] h CLICON handle
* @param[in] session_id Id of session to kill
*/
int
clicon_rpc_kill(clicon_handle h,
int session_id)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_kill_encode(session_id, __FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send a debug request to backend server
* @param[in] h CLICON handle
* @param[in] level Debug level
*/
int
clicon_rpc_debug(clicon_handle h,
int level)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_debug_encode(level, __FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! An rpc call from a frontend module to a function in a backend module
*
* A CLI/netconf frontend module can make a functional call to a backend
* module and get return value back.
* The backend module needs to be specified (XXX would be nice to avoid this)
* parameters can be sent, and value returned.
* A function (func) must be defined in the backend module (plugin)
* An example signature of such a downcall function is:
* @code
* char name[16];
* char *ret;
* uint16_t retlen;
* clicon_rpc_call(h, 0, "my-backend-plugin", "my_fn", name, 16,
* &ret, &retlen, __FUNCTION__);
* unchunk_group(__FUNCTION__); # deallocate 'ret'
* @endcode
* Backend example function:
* @code
int
downcall(clicon_handle h, uint16_t op, uint16_t len, void *arg,
uint16_t *reply_data_len, void **reply_data)
* @endcode
*
* @param[in] h Clicon handle
* @param[in] op Generic application-defined operation
* @param[in] plugin Name of backend plugin (XXX look in backend plugin dir)
* @param[in] func Name of function i backend (ie downcall above) as string
* @param[in] param Input parameter given to function (void* arg in downcall)
* @param[in] paramlen Length of input parameter
* @param[out] ret Returned data as byte-string. Deallocate w unchunk...(..., label)
* @param[out] retlen Length of returned data
* @param[in] label Label used in chunk (de)allocation. Use:
* unchunk_group(label) to deallocate
*/
int
clicon_rpc_call(clicon_handle h, uint16_t op, char *plugin, char *func,
void *param, uint16_t paramlen,
char **ret, uint16_t *retlen,
const void *label)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg = clicon_msg_call_encode(op, plugin, func,
paramlen, param,
label)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, (char**)ret, retlen, NULL, label) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Create a new notification subscription
* @param[in] h Clicon handle
* @param[in] status 0: stop existing notification stream 1: start new stream.
* @param{in] stream name of notificatio/log stream (CLICON is predefined)
* @param{in] filter message filter, eg xpath for xml notifications
* @param[out] s0 socket returned where notification mesages will appear
*/
int
clicon_rpc_subscription(clicon_handle h,
int status,
char *stream,
enum format_enum format,
char *filter,
int *s0)
{
struct clicon_msg *msg;
int retval = -1;
if ((msg=clicon_msg_subscription_encode(status, stream, format, filter,
__FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, s0, __FUNCTION__) < 0)
goto done;
if (status == 0){
close(*s0);
*s0 = -1;
}
done:
unchunk_group(__FUNCTION__);
return retval;
}

File diff suppressed because it is too large Load diff

462
lib/src/clicon_qdb.c Normal file
View file

@ -0,0 +1,462 @@
/*
*
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"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <sys/types.h>
#include <limits.h>
#include <regex.h>
#include <sys/stat.h>
#include <sys/param.h>
#if defined(HAVE_DEPOT_H) || defined(HAVE_QDBM_DEPOT_H)
#ifdef HAVE_DEPOT_H
#include <depot.h> /* qdb api */
#else /* HAVE_QDBM_DEPOT_H */
#include <qdbm/depot.h> /* qdb api */
#endif
#include <cligen/cligen.h>
/* clicon */
#include "clicon_log.h"
#include "clicon_err.h"
#include "clicon_queue.h"
#include "clicon_chunk.h"
#include "clicon_qdb.h"
/*! Initialize database
* @param[in] file database file
* @param[in] omode see man dpopen
*/
static int
db_init_mode(char *file, int omode)
{
DEPOT *dp;
/* Open database for writing */
if ((dp = dpopen(file, omode | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, 0, "db_init: dpopen(%s): %s",
file,
dperrmsg(dpecode));
return -1;
}
clicon_debug(1, "db_init(%s)", file);
if (dpclose(dp) == 0){
clicon_err(OE_DB, 0, "db_set: dpclose: %s", dperrmsg(dpecode));
return -1;
}
return 0;
}
/*! Open database for reading and writing
* @param[in] file database file
*/
int
db_init(char *file)
{
return db_init_mode(file, DP_OWRITER | DP_OCREAT ); /* DP_OTRUNC? */
}
/*! Write data to database
* @param[in] file database file
* @param[in] key database key
* @param[out] data Buffer containing content
* @param[out] datalen Length of buffer
* @retval 0 if OK: value returned. If not found, zero string returned
* @retval -1 on error
*/
int
db_set(char *file, char *key, void *data, size_t datalen)
{
DEPOT *dp;
/* Open database for writing */
if ((dp = dpopen(file, DP_OWRITER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, 0, "db_set: dpopen(%s): %s",
file,
dperrmsg(dpecode));
return -1;
}
clicon_debug(2, "%s: db_put(%s, len:%d)",
file, key, (int)datalen);
if (dpput(dp, key, -1, data, datalen, DP_DOVER) == 0){
clicon_err(OE_DB, 0, "%s: db_set: dpput(%s, %d): %s",
file,
key,
datalen,
dperrmsg(dpecode));
dpclose(dp);
return -1;
}
if (dpclose(dp) == 0){
clicon_err(OE_DB, 0, "db_set: dpclose: %s", dperrmsg(dpecode));
return -1;
}
return 0;
}
/*! Get data from database
* @param[in] file database file
* @param[in] key database key
* @param[out] data Pre-allocated buffer where data corresponding key is placed
* @param[out] datalen Length of pre-allocated buffer
* @retval 0 if OK: value returned. If not found, zero string returned
* @retval -1 on error
* @see db_get_alloc Allocates memory
*/
int
db_get(char *file,
char *key,
void *data,
size_t *datalen)
{
DEPOT *dp;
int len;
/* Open database for readinf */
if ((dp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, dpecode, "%s: db_get(%s, %d): dpopen: %s",
file,
key,
datalen,
dperrmsg(dpecode));
return -1;
}
len = dpgetwb(dp, key, -1, 0, *datalen, data);
if (len < 0){
if (dpecode == DP_ENOITEM){
data = NULL;
*datalen = 0;
}
else{
clicon_err(OE_DB, 0, "db_get: dpgetwb: %s (%d)",
dperrmsg(dpecode), dpecode);
dpclose(dp);
return -1;
}
}
else
*datalen = len;
clicon_debug(2, "db_get(%s, %s)=%s", file, key, (char*)data);
if (dpclose(dp) == 0){
clicon_err(OE_DB, 0, "db_get: dpclose: %s", dperrmsg(dpecode));
return -1;
}
return 0;
}
/*! Get data from database and allocates memory
* Similar to db_get but returns a malloced pointer to the data instead
* of copying data to pre-allocated buffer. This is necessary if the
* length of the data is not known when calling the function.
* @param[in] file database file
* @param[in] key database key
* @param[out] data Allocated buffer where data corresponding key is placed
* @param[out] datalen Length of pre-allocated buffer
* @retval 0 if OK: value returned. If not found, zero string returned
* @retval -1 on error
* @note: *data needs to be freed after use.
* @code
* char *lvec = NULL;
* size_t len = 0;
* if (db_get-alloc(dbname, "a.0", &val, &vlen) == NULL)
* return -1;
* ..do stuff..
* if (val) free(val);
* @endcode
* @see db_get Pre-allocates memory
*/
int
db_get_alloc(char *file,
char *key,
void **data,
size_t *datalen)
{
DEPOT *dp;
int len;
/* Open database for writing */
if ((dp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, 0, "%s: dpopen(%s): %s",
__FUNCTION__,
file,
dperrmsg(dpecode));
return -1;
}
if ((*data = dpget(dp, key, -1, 0, -1, &len)) == NULL){
if (dpecode == DP_ENOITEM){
*datalen = 0;
*data = NULL;
len = 0;
}
else{
/* No entry vs error? */
clicon_err(OE_DB, 0, "db_get_alloc: dpgetwb: %s (%d)",
dperrmsg(dpecode), dpecode);
dpclose(dp);
return -1;
}
}
*datalen = len;
if (dpclose(dp) == 0){
clicon_err(OE_DB, 0, "db_get_alloc: dpclose: %s", dperrmsg(dpecode));
return -1;
}
return 0;
}
/*! Delete database entry
* @param[in] file database file
* @param[in] key database key
* @retval -1 on failure,
* @retval 0 if key did not exist
* @retval 1 if successful.
*/
int
db_del(char *file, char *key)
{
int retval = 0;
DEPOT *dp;
/* Open database for writing */
if ((dp = dpopen(file, DP_OWRITER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, 0, "db_del: dpopen(%s): %s",
file,
dperrmsg(dpecode));
return -1;
}
if (dpout(dp, key, -1)) {
retval = 1;
}
if (dpclose(dp) == 0){
clicon_err(OE_DB, 0, "db_del: dpclose: %s", dperrmsg(dpecode));
return -1;
}
return retval;
}
/*! Check if entry in database exists
* @param[in] file database file
* @param[in] key database key
* @retval 1 if key exists in database
* @retval 0 key does not exist in database
* @retval -1 error
*/
int
db_exists(char *file, char *key)
{
DEPOT *dp;
int len;
/* Open database for reading */
if ((dp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, dpecode, "%s: dpopen: %s",
__FUNCTION__, dperrmsg(dpecode));
return -1;
}
len = dpvsiz(dp, key, -1);
if (len < 0 && dpecode != DP_ENOITEM)
clicon_err(OE_DB, 0, "^s: dpvsiz: %s (%d)",
__FUNCTION__, dperrmsg(dpecode), dpecode);
if (dpclose(dp) == 0) {
clicon_err(OE_DB, 0, "%s: dpclose: %s", dperrmsg(dpecode),__FUNCTION__);
return -1;
}
return (len < 0) ? 0 : 1;
}
/*! Return all entries in database that match a regular expression.
* @param[in] file database file
* @param[in] regexp regular expression for database keys
* @param[in] label for memory/chunk allocation
* @param[out] pairs Vector of database keys and values
* @param[in] noval If set don't retreive values, just keys
* @retval -1 on error
* @retval n Number of pairs
* @code
* struct db_pair *pairs;
* int npairs;
* if ((npairs = db_regexp(dbname, "^/test/kalle$", __FUNCTION__,
* &pairs, 0)) < 0)
* err;
*
* @endcode
*/
int
db_regexp(char *file,
char *regexp,
const char *label,
struct db_pair **pairs,
int noval)
{
int npairs;
int status;
int retval = -1;
int vlen = 0;
char *key = NULL;
void *val = NULL;
char errbuf[512];
struct db_pair *pair;
struct db_pair *newpairs;
regex_t iterre;
DEPOT *iterdp = NULL;
regmatch_t pmatch[1];
size_t nmatch = 1;
npairs = 0;
*pairs = NULL;
if (regexp) {
if ((status = regcomp(&iterre, regexp, REG_EXTENDED)) != 0) {
regerror(status, &iterre, errbuf, sizeof(errbuf));
clicon_err(OE_DB, 0, "%s: regcomp: %s", __FUNCTION__, errbuf);
return -1;
}
}
/* Open database for reading */
if ((iterdp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, 0, "%s: dpopen(%s): %s",
__FUNCTION__, file, dperrmsg(dpecode));
goto quit;
}
/* Initiate iterator */
if(dpiterinit(iterdp) == 0) {
clicon_err(OE_DB, 0, "%s: dpiterinit: %s", __FUNCTION__, dperrmsg(dpecode));
goto quit;
}
/* Iterate through DB */
while((key = dpiternext(iterdp, NULL)) != NULL) {
if (regexp && regexec(&iterre, key, nmatch, pmatch, 0) != 0) {
free(key);
continue;
}
/* Retrieve value if required */
if ( ! noval) {
if((val = dpget(iterdp, key, -1, 0, -1, &vlen)) == NULL) {
clicon_log(OE_DB, "%s: dpget: %s", __FUNCTION__, dperrmsg(dpecode));
goto quit;
}
}
/* Resize and populate resulting array */
newpairs = rechunk(*pairs, (npairs+1) * sizeof(struct db_pair), label);
if (newpairs == NULL) {
clicon_err(OE_DB, errno, "%s: rechunk", __FUNCTION__);
goto quit;
}
pair = &newpairs[npairs];
memset (pair, 0, sizeof(*pair));
pair->dp_key = chunk_sprintf(label, "%s", key);
if (regexp)
pair->dp_matched = chunk_sprintf(label, "%.*s",
pmatch[0].rm_eo - pmatch[0].rm_so,
key + pmatch[0].rm_so);
else
pair->dp_matched = chunk_sprintf(label, "%s", key);
if (pair->dp_key == NULL || pair->dp_matched == NULL) {
clicon_err(OE_DB, errno, "%s: chunk_sprintf");
goto quit;
}
if ( ! noval) {
if (vlen){
pair->dp_val = chunkdup (val, vlen, label);
if (pair->dp_val == NULL) {
clicon_err(OE_DB, errno, "%s: chunkdup", __FUNCTION__);
goto quit;
}
}
pair->dp_vlen = vlen;
free(val);
val = NULL;
}
(*pairs) = newpairs;
npairs++;
free(key);
}
retval = npairs;
quit:
if (key)
free(key);
if (val)
free(val);
if (regexp)
regfree(&iterre);
if (iterdp)
dpclose(iterdp);
if (retval < 0)
unchunk_group(label);
return retval;
}
/*! Sanitize regexp string. Escape '\' etc.
*/
char *
db_sanitize(char *rx, const char *label)
{
char *new;
char *k, *p, *s;
k = chunk_sprintf(__FUNCTION__, "%s", "");
p = rx;
while((s = strstr(p, "\\"))) {
if ((k = chunk_sprintf(__FUNCTION__, "%s%.*s\\\\", k, s-p, p)) == NULL)
goto quit;
p = s+1;
}
if ((k = chunk_strncat(k, p, strlen(p), __FUNCTION__)) == NULL)
goto quit;
new = (char *)chunkdup(k, strlen(k)+1, label);
unchunk_group(__FUNCTION__);
return new;
quit:
unchunk_group(__FUNCTION__);
return NULL;
}
#endif /* DEPOT */

448
lib/src/clicon_sha1.c Normal file
View file

@ -0,0 +1,448 @@
/*
* sha1.c
*
* Copyright (C) 1998, 2009
* Paul E. Jones <paulej@packetizer.com>
* All Rights Reserved
*
* Freeware Public License (FPL)
*
* This software is licensed as "freeware." Permission to distribute
* this software in source and binary forms, including incorporation
* into other products, is hereby granted without a fee. THIS SOFTWARE
* IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHOR SHALL NOT BE HELD
* LIABLE FOR ANY DAMAGES RESULTING FROM THE USE OF THIS SOFTWARE, EITHER
* DIRECTLY OR INDIRECTLY, INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA
* OR DATA BEING RENDERED INACCURATE.
*
*****************************************************************************
* $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $
*****************************************************************************
*
* Description:
* This file implements the Secure Hashing Standard as defined
* in FIPS PUB 180-1 published April 17, 1995.
*
* The Secure Hashing Standard, which uses the Secure Hashing
* Algorithm (SHA), produces a 160-bit message digest for a
* given data stream. In theory, it is highly improbable that
* two messages will produce the same message digest. Therefore,
* this algorithm can serve as a means of providing a "fingerprint"
* for a message.
*
* Portability Issues:
* SHA-1 is defined in terms of 32-bit "words". This code was
* written with the expectation that the processor has at least
* a 32-bit machine word size. If the machine word size is larger,
* the code should still function properly. One caveat to that
* is that the input functions taking characters and character
* arrays assume that only 8 bits of information are stored in each
* character.
*
* Caveats:
* SHA-1 is designed to work with messages less than 2^64 bits
* long. Although SHA-1 allows a message digest to be generated for
* messages of any number of bits less than 2^64, this
* implementation only works with messages with a length that is a
* multiple of the size of an 8-bit character.
*
*/
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <errno.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clicon_log.h"
#include "clicon_err.h"
/*
* This structure will hold context information for the hashing
* operation
*/
typedef struct SHA1Context
{
uint32_t Message_Digest[5]; /* Message Digest (output) */
uint32_t Length_Low; /* Message length in bits */
uint32_t Length_High; /* Message length in bits */
unsigned char Message_Block[64]; /* 512-bit message blocks */
int Message_Block_Index; /* Index into message block array */
int Computed; /* Is the digest computed? */
int Corrupted; /* Is the message digest corruped? */
} SHA1Context;
/*
* Define the circular shift macro
*/
#define SHA1CircularShift(bits,word) \
((((word) << (bits)) & 0xFFFFFFFF) | \
((word) >> (32-(bits))))
/* Function prototypes */
static void SHA1ProcessMessageBlock(SHA1Context *);
static void SHA1PadMessage(SHA1Context *);
static void SHA1Reset(SHA1Context *);
static int SHA1Result(SHA1Context *);
static void SHA1Input( SHA1Context *, const unsigned char *, uint32_t);
/*
* SHA1Reset
*
* Description:
* This function will initialize the SHA1Context in preparation
* for computing a new message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* Nothing.
*
* Comments:
*
*/
static void
SHA1Reset(SHA1Context *context)
{
context->Length_Low = 0;
context->Length_High = 0;
context->Message_Block_Index = 0;
context->Message_Digest[0] = 0x67452301;
context->Message_Digest[1] = 0xEFCDAB89;
context->Message_Digest[2] = 0x98BADCFE;
context->Message_Digest[3] = 0x10325476;
context->Message_Digest[4] = 0xC3D2E1F0;
context->Computed = 0;
context->Corrupted = 0;
}
/*
* SHA1Result
*
* Description:
* This function will return the 160-bit message digest into the
* Message_Digest array within the SHA1Context provided
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA-1 hash.
*
* Returns:
* 1 if successful, 0 if it failed.
*
* Comments:
*
*/
static int
SHA1Result(SHA1Context *context)
{
if (context->Corrupted)
{
return 0;
}
if (!context->Computed)
{
SHA1PadMessage(context);
context->Computed = 1;
}
return 1;
}
/*
* SHA1Input
*
* Description:
* This function accepts an array of octets as the next portion of
* the message.
*
* Parameters:
* context: [in/out]
* The SHA-1 context to update
* message_array: [in]
* An array of characters representing the next portion of the
* message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* Nothing.
*
* Comments:
*
*/
static void
SHA1Input( SHA1Context *context,
const unsigned char *message_array,
uint32_t length)
{
if (!length)
{
return;
}
if (context->Computed || context->Corrupted)
{
context->Corrupted = 1;
return;
}
while(length-- && !context->Corrupted)
{
context->Message_Block[context->Message_Block_Index++] =
(*message_array & 0xFF);
context->Length_Low += 8;
/* Force it to 32 bits */
context->Length_Low &= 0xFFFFFFFF;
if (context->Length_Low == 0)
{
context->Length_High++;
/* Force it to 32 bits */
context->Length_High &= 0xFFFFFFFF;
if (context->Length_High == 0)
{
/* Message is too long */
context->Corrupted = 1;
}
}
if (context->Message_Block_Index == 64)
{
SHA1ProcessMessageBlock(context);
}
message_array++;
}
}
/*
* SHA1ProcessMessageBlock
*
* Description:
* This function will process the next 512 bits of the message
* stored in the Message_Block array.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in the SHAContext, especially the
* single character names, were used because those were the names
* used in the publication.
*
*
*/
static void
SHA1ProcessMessageBlock(SHA1Context *context)
{
const uint32_t K[] = /* Constants defined in SHA-1 */
{
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6
};
int t; /* Loop counter */
uint32_t temp; /* Temporary word value */
uint32_t W[80]; /* Word sequence */
uint32_t A, B, C, D, E; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for(t = 0; t < 16; t++)
{
W[t] = ((uint32_t) context->Message_Block[t * 4]) << 24;
W[t] |= ((uint32_t) context->Message_Block[t * 4 + 1]) << 16;
W[t] |= ((uint32_t) context->Message_Block[t * 4 + 2]) << 8;
W[t] |= ((uint32_t) context->Message_Block[t * 4 + 3]);
}
for(t = 16; t < 80; t++)
{
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
}
A = context->Message_Digest[0];
B = context->Message_Digest[1];
C = context->Message_Digest[2];
D = context->Message_Digest[3];
E = context->Message_Digest[4];
for(t = 0; t < 20; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | ((~B) & D)) + E + W[t] + K[0];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 20; t < 40; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 40; t < 60; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 60; t < 80; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
context->Message_Digest[0] =
(context->Message_Digest[0] + A) & 0xFFFFFFFF;
context->Message_Digest[1] =
(context->Message_Digest[1] + B) & 0xFFFFFFFF;
context->Message_Digest[2] =
(context->Message_Digest[2] + C) & 0xFFFFFFFF;
context->Message_Digest[3] =
(context->Message_Digest[3] + D) & 0xFFFFFFFF;
context->Message_Digest[4] =
(context->Message_Digest[4] + E) & 0xFFFFFFFF;
context->Message_Block_Index = 0;
}
/*
* SHA1PadMessage
*
* Description:
* According to the standard, the message must be padded to an even
* 512 bits. The first padding bit must be a '1'. The last 64
* bits represent the length of the original message. All bits in
* between should be 0. This function will pad the message
* according to those rules by filling the Message_Block array
* accordingly. It will also call SHA1ProcessMessageBlock()
* appropriately. When it returns, it can be assumed that the
* message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad
*
* Returns:
* Nothing.
*
* Comments:
*
*/
static void
SHA1PadMessage(SHA1Context *context)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (context->Message_Block_Index > 55)
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 64)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
SHA1ProcessMessageBlock(context);
while(context->Message_Block_Index < 56)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
}
else
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 56)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
}
/*
* Store the message length as the last 8 octets
*/
context->Message_Block[56] = (context->Length_High >> 24) & 0xFF;
context->Message_Block[57] = (context->Length_High >> 16) & 0xFF;
context->Message_Block[58] = (context->Length_High >> 8) & 0xFF;
context->Message_Block[59] = (context->Length_High) & 0xFF;
context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF;
context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF;
context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF;
context->Message_Block[63] = (context->Length_Low) & 0xFF;
SHA1ProcessMessageBlock(context);
}
char *
clicon_sha1hex(const char *str)
{
int i;
char *retstr;
SHA1Context context;
SHA1Reset(&context);
SHA1Input(&context, (const unsigned char *)str, strlen(str));
if (!SHA1Result(&context)) {
clicon_err(OE_UNIX, 0, "Could not compute SHA1 message digest");
return NULL;
}
/* 160 bits = 20 bytes = 40 hexdigits + '\0' */
if ((retstr = malloc(41)) == NULL) {
clicon_err(OE_UNIX, errno, "malloc");
return NULL;
}
for(i = 0; i < 5 ; i++)
sprintf(retstr + (i*8), "%.8x", context.Message_Digest[i]);
return retstr;
}

185
lib/src/clicon_sig.c Normal file
View file

@ -0,0 +1,185 @@
/*
*
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 <signal.h>
#include <syslog.h>
#include <errno.h>
/* clicon */
#include "clicon_err.h"
#include "clicon_log.h"
#include "clicon_sig.h"
/*
* Set a signal handler.
*/
int
set_signal(int signo, void (*handler)(int), void (**oldhandler)(int))
{
#if defined(HAVE_SIGACTION)
struct sigaction sold, snew;
snew.sa_handler = handler;
sigemptyset(&snew.sa_mask);
snew.sa_flags = 0;
if (sigaction (signo, &snew, &sold) < 0){
clicon_err(OE_UNIX, errno, "sigaction");
return -1;
}
if (oldhandler)
*oldhandler = sold.sa_handler;
return 0;
#elif defined(HAVE_SIGVEC)
assert(0);
return 0;
#endif
}
/*
* Block signal. If 'sig' is 0, block all signals
*/
void
clicon_signal_block (int sig)
{
sigset_t
set;
sigemptyset (&set);
if (sig)
sigaddset (&set, sig);
else
sigfillset (&set);
sigprocmask (SIG_BLOCK, &set, NULL);
}
/*
* Unblock signal. If 'sig' is 0, unblock all signals
*/
void
clicon_signal_unblock (int sig)
{
sigset_t
set;
sigemptyset (&set);
if (sig)
sigaddset (&set, sig);
else
sigfillset (&set);
sigprocmask (SIG_UNBLOCK, &set, NULL);
}
/*! Read pidfile and return pid, if any
*
* @param[in] pidfile Name of pidfile
* @param[out] pid0 Process id of (eventual) existing daemon process
* @retval 0 OK. if pid > 0 old process exists w that pid
* @retval -1 Error, and clicon_err() called
*/
int
pidfile_get(char *pidfile, pid_t *pid0)
{
FILE *f;
char *ptr;
char buf[32];
pid_t pid;
if ((f = fopen (pidfile, "r")) != NULL){
ptr = fgets(buf, sizeof(buf), f);
fclose (f);
if (ptr != NULL && (pid = atoi (ptr)) > 1) {
if (kill (pid, 0) == 0 || errno != ESRCH) {
/* Yes there is a process */
*pid0 = pid;
return 0;
}
}
}
*pid0 = 0;
return 0;
}
/*! Given a pid, kill that process
*
* @param[in] pid Process id
* @retval 0 Killed OK
* @retval -1 Could not kill.
* Maybe shouldk not belong to pidfile code,..
*/
int
pidfile_zapold(pid_t pid)
{
clicon_log(LOG_NOTICE, "Killing old daemon with pid: %d", pid);
killpg(pid, SIGTERM);
kill(pid, SIGTERM);
sleep(1); /* check again */
if ((kill (pid, 0)) != 0 && errno == ESRCH) /* Nothing there */
;
else{ /* problem: couldnt kill it */
clicon_err(OE_DEMON, errno, "Killing old demon");
return -1;
}
return 0;
}
/*! Write a pid-file
*
* @param[in] pidfile Name of pidfile
* @param[in] kill_it If set, kill existing process otherwise return pid
*/
int
pidfile_write(char *pidfile)
{
FILE *f = NULL;
int retval = -1;
/* Here, there should be no old agent and no pidfile */
if ((f = fopen(pidfile, "wb")) == NULL){
if (errno == EACCES)
clicon_err(OE_DEMON, errno, "Creating pid-file %s (Try run as root?)", pidfile);
else
clicon_err(OE_DEMON, errno, "Creating pid-file %s", pidfile);
goto done;
}
if ((retval = fprintf(f, "%ld\n", (long) getpid())) < 1){
clicon_err(OE_DEMON, errno, "Could not write pid to %s", pidfile);
goto done;
}
clicon_debug(1, "Opened pidfile %s with pid %d", pidfile, getpid());
retval = 0;
done:
if (f != NULL)
fclose(f);
return retval;
}

346
lib/src/clicon_string.c Normal file
View file

@ -0,0 +1,346 @@
/*
*
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/>.
*/
/* Error handling: dont use clicon_err, treat as unix system calls. That is,
ensure errno is set and return -1/NULL */
#ifdef HAVE_CONFIG_H
#include "clicon_config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <regex.h>
#include <ctype.h>
/* clicon */
#include "clicon_queue.h"
#include "clicon_chunk.h"
#include "clicon_string.h"
#include "clicon_err.h"
/*! Split string into a vector based on character delimiters
*
* The given string is split into a vector where the delimiter can be
* any of the characters in the specified delimiter string.
*
* See also clicon_strsplit() which is similar by operates on a full string
* delimiter rather than individual character delimiters.
*
* The vector returned is one single memory chunk that must be unchunked
* by the caller
*
* @param string String to be split
* @param delim String of delimiter characters
* @param nvec Number of entries in returned vector
* @param label Chunk label for returned vector
*/
char **
clicon_sepsplit (char *string, char *delim, int *nvec, const char *label)
{
int idx;
size_t siz;
char *s, *s0;
char **vec, *vecp;
*nvec = 0;
s0 = s = chunkdup (string, strlen(string)+1, __FUNCTION__);
while (strsep(&s, delim))
(*nvec)++;
unchunk (s0);
siz = ((*nvec +1) * sizeof (char *)) + strlen(string) + 1;
vec = (char **) chunk (siz, label);
if (!vec) {
return NULL;
}
bzero (vec, siz);
vecp = (char *)&vec[*nvec +1];
bcopy (string, vecp, strlen (string));
for (idx = 0; idx < *nvec; idx++) {
vec[idx] = vecp;
strsep (&vecp, delim);
}
return vec;
}
/*! Split string into a vector based on a string delimiter
*
* The given string is split into a vector where the delimited by the
* the full delimiter string. The matched delimiters are not part of the
* resulting vector.
*
* See also clicon_sepsplit() which is similar by operates on individual
* character delimiters rather then a full string delimiter.
*
* The vector returned is one single memory chunk that must be unchunked
* by the caller
*
* @param string String to be split
* @param delim String of delimiter characters
* @param nvec Number of entries in returned vector
* @param label Chunk label for returned vector
*/
char **
clicon_strsplit (char *string, char *delim, int *nvec, const char *label)
{
int idx;
size_t siz;
char *s;
char **vec, *vecp;
*nvec = 1;
s = string;
while ((s = strstr(s, delim))) {
s += strlen(delim);
(*nvec)++;
}
siz = ((*nvec +1) * sizeof (char *)) + strlen(string) + 1;
vec = (char **) chunk (siz, label);
if (!vec) {
return NULL;
}
bzero (vec, siz);
vecp = (char *)&vec[*nvec +1];
bcopy (string, vecp, strlen (string));
s = vecp;
for (idx = 0; idx < *nvec; idx++) {
vec[idx] = s;
if ((s = strstr(s, delim)) != NULL) {
*s = '\0';
s += strlen(delim);
}
}
return vec;
}
/*
* Concatenate elements of a string array into a string. An optiona delimiter
* string can be specified which will be inserted betwen each element.
* Resulting string is chunk:ed using the specified group label and need to be
* unchunked by the caller
*/
char *
clicon_strjoin (int argc, char **argv, char *delim, const char *label)
{
int i;
int len;
char *str;
len = 0;
for (i = 0; i < argc; i++)
len += strlen(argv[i]);
if (delim)
len += (strlen(delim) * argc);
len += 1; /* '\0' */
if ((str = chunk (len, label)) == NULL)
return NULL;
memset (str, '\0', len);
for (i = 0; i < argc; i++) {
if (i != 0)
strncat (str, delim, len - strlen(str));
strncat (str, argv[i], len - strlen(str));
}
return str;
}
/*
* Trim of whitespace in beginning and end of string.
* A new string is returned, chunked with specified label
*/
char *
clicon_strtrim(char *str, const char *label)
{
char *start, *end, *new;
start = str;
while (*start != '\0' && isspace(*start))
start++;
if (!strlen(start))
return (char *)chunkdup("\0", 1, label);
end = str + strlen(str) ;
while (end > str && isspace(*(end-1)))
end--;
if((new = chunkdup (start, end-start+1, label)))
new[end-start] = '\0';
return new;
}
/*
* clicon_sep
* given a string s, on format: a[b], separate it into two parts: a and b
* [] are separators.
* alterative use:
* a/b -> a and b (where sep = "/")
*/
int
clicon_sep(char *s, const char sep[2], const char *label, char**a0, char **b0)
{
char *a = NULL;
char *b = NULL;
char *ptr;
int len;
int retval = -1;
ptr = s;
/* move forward to last char of element name */
while (*ptr && *ptr != sep[0] && *ptr != sep[1] )
ptr++;
/* Copy first element name */
len = ptr-s;
if ((a = chunkdup(s, len+1, label)) == NULL)
goto catch;
a[len] = '\0';
/* Do we have an extended format? */
if (*ptr == sep[0]) {
b = ++ptr;
/* move forward to end extension */
while (*ptr && *ptr != sep[1])
ptr++;
/* Copy extension */
len = ptr-b;
if ((b = chunkdup(b, len+1, label)) == NULL)
goto catch;
b[len] = '\0';
}
*a0 = a;
*b0 = b;
retval = 0;
catch:
return retval;
}
/*
* strndup() for systems without it, such as xBSD
*/
#ifndef HAVE_STRNDUP
char *
clicon_strndup (const char *str, size_t len)
{
char *new;
size_t slen;
slen = strlen (str);
len = (len < slen ? len : slen);
new = malloc (len + 1);
if (new == NULL)
return NULL;
new[len] = '\0';
memcpy (new, str, len);
return new;
}
#endif /* ! HAVE_STRNDUP */
/*
* clicon_strmatch - Match string against regexp.
*
* Returns -1 on failure, 0 on no matach or >0 (length of matching substring)
* in case of a match. If a match pointer is given, the matching substring
* will be allocated 'match' will be pointing to it. The match string must
* be free:ed by the application.
*/
int
clicon_strmatch(const char *str, const char *regexp, char **match)
{
size_t len;
int status;
regex_t re;
char rxerr[128];
size_t nmatch = 1;
regmatch_t pmatch[1];
if (match)
*match = NULL;
if ((status = regcomp(&re, regexp, REG_EXTENDED)) != 0) {
regerror(status, &re, rxerr, sizeof(rxerr));
clicon_err(OE_REGEX, errno, "%s", rxerr);
return -1;
}
status = regexec(&re, str, nmatch, pmatch, 0);
regfree(&re);
if (status != 0)
return 0; /* No match */
len = pmatch[0].rm_eo - pmatch[0].rm_so;
/* If we've specified a match pointer, allocate and populate it. */
if (match) {
if ((*match = malloc(len + 1)) == NULL) {
clicon_err(OE_UNIX, errno, "Failed to allocate string");
return -1;
}
memset(*match, '\0', len + 1);
strncpy(*match, str + pmatch[0].rm_so, len);
}
return len;
}
/*
* clicon_strsub - substitute pattern in string.
* Returns new malloc:ed string on success or NULL on failure.
*/
char *
clicon_strsub(char *str, char *from, char *to)
{
char **vec;
int nvec;
char *new;
char *retval = NULL;
if ((vec = clicon_strsplit(str, from, &nvec, __FUNCTION__)) == NULL) {
clicon_err(OE_UNIX, errno, "Failed to split string");
goto done;
}
if ((new = clicon_strjoin (nvec, vec, to, __FUNCTION__)) == NULL) {
clicon_err(OE_UNIX, errno, "Failed to split string");
goto done;
}
retval = strdup(new);
done:
unchunk_group(__FUNCTION__);
return retval;
}

1039
lib/src/clicon_xml.c Normal file

File diff suppressed because it is too large Load diff

1229
lib/src/clicon_xml_db.c Normal file

File diff suppressed because it is too large Load diff

845
lib/src/clicon_xml_map.c Normal file
View file

@ -0,0 +1,845 @@
/*
*
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/>.
*
* XML code
*
* file
* +---------+ db2xml_key-> save_db_to_xml->
* +-------------> | database| <------------+------------------+
* | +---------+ <-xml2db | <-load_xml_to_db |
* | | |
* | | |
* v v v
* +---------+ <-xml2cvec_key +-----------+ +---------+
* | cvec | <---------------------> | xml cxobj |<--------->| xmlfile |
* +---------+ cvec2xml-> +-----------+ +---------+
* cvec2xml_1(yang)-> xml2json->|
* xml2txt-> |
* xml2cli-> v
* +---------+
* | file |
* +---------+
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <arpa/inet.h>
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
#include <netinet/in.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clicon_string.h"
#include "clicon_queue.h"
#include "clicon_hash.h"
#include "clicon_chunk.h"
#include "clicon_handle.h"
#include "clicon_yang.h"
#include "clicon_yang_type.h"
#include "clicon_options.h"
#include "clicon_qdb.h"
#include "clicon_xml.h"
#include "clicon_xsl.h"
#include "clicon_log.h"
#include "clicon_err.h"
#include "clicon_xml_map.h"
/* Something to do with reverse engineering of junos syntax? */
#undef SPECIAL_TREATMENT_OF_NAME
/*
* A node is a leaf if it contains a body.
*/
static cxobj *
leaf(cxobj *xn)
{
cxobj *xc = NULL;
while ((xc = xml_child_each(xn, xc, CX_BODY)) != NULL)
break;
return xc;
}
/*! x is element and has eactly one child which in turn has none */
static int
tleaf(cxobj *x)
{
cxobj *c;
if (xml_type(x) != CX_ELMNT)
return 0;
if (xml_child_nr(x) != 1)
return 0;
c = xml_child_i(x, 0);
return (xml_child_nr(c) == 0);
}
/*! Translate XML -> TEXT
* @param[in] level print 4 spaces per level in front of each line
*/
int
xml2txt(FILE *f, cxobj *x, int level)
{
cxobj *xe = NULL;
int children=0;
char *term;
int retval = -1;
#ifdef SPECIAL_TREATMENT_OF_NAME
cxobj *xname;
#endif
xe = NULL; /* count children */
while ((xe = xml_child_each(x, xe, -1)) != NULL)
children++;
if (!children){
if (xml_type(x) == CX_BODY){
/* Kludge for escaping encrypted passwords */
if (strcmp(xml_name(xml_parent(x)), "encrypted-password")==0)
term = chunk_sprintf(__FUNCTION__, "\"%s\"", xml_value(x));
else
term = xml_value(x);
}
else{
fprintf(f, "%*s", 4*level, "");
term = xml_name(x);
}
fprintf(f, "%s;\n", term);
retval = 0;
goto done;
}
fprintf(f, "%*s", 4*level, "");
#ifdef SPECIAL_TREATMENT_OF_NAME
if (strcmp(xml_name(x), "name") != 0)
fprintf(f, "%s ", xml_name(x));
if ((xname = xml_find(x, "name")) != NULL){
if (children > 1)
fprintf(f, "%s ", xml_body(xname));
if (!tleaf(x))
fprintf(f, "{\n");
}
else
if (!tleaf(x))
fprintf(f, "{\n");
#else
fprintf(f, "%s ", xml_name(x));
if (!tleaf(x))
fprintf(f, "{\n");
#endif /* SPECIAL_TREATMENT_OF_NAME */
xe = NULL;
while ((xe = xml_child_each(x, xe, -1)) != NULL){
#ifdef SPECIAL_TREATMENT_OF_NAME
if (xml_type(xe) == CX_ELMNT && (strcmp(xml_name(xe), "name")==0) &&
(children > 1)) /* skip if this is a name element (unless 0 children) */
continue;
#endif
if (xml2txt(f, xe, level+1) < 0)
break;
}
if (!tleaf(x))
fprintf(f, "%*s}\n", 4*level, "");
retval = 0;
done:
return retval;
}
/*! Translate from XML to CLI commands
* Howto: join strings and pass them down.
* Identify unique/index keywords for correct set syntax.
* Args:
* @param[in] f Where to print cli commands
* @param[in] x XML Parse-tree (to translate)
* @param[in] prepend0 Print this text in front of all commands.
* @param[in] gt option to steer cli syntax
* @param[in] label Memory chunk allocation label
*/
int
xml2cli(FILE *f,
cxobj *x,
char *prepend0,
enum genmodel_type gt,
const char *label)
{
int retval = -1;
cxobj *xe = NULL;
char *term;
char *prepend;
int bool;
int nr;
int i;
nr = xml_child_nr(x);
if (!nr){
if (xml_type(x) == CX_BODY)
term = xml_value(x);
else
term = xml_name(x);
if (prepend0)
fprintf(f, "%s ", prepend0);
fprintf(f, "%s\n", term);
retval = 0;
goto done;
}
prepend = "";
if (prepend0)
if ((prepend = chunk_sprintf(label, "%s%s", prepend, prepend0)) == NULL)
goto done;
/* bool determines when to print a variable keyword:
!leaf T for all (ie parameter)
index GT_NONE F
index GT_VARS F
index GT_ALL T
!index GT_NONE F
!index GT_VARS T
!index GT_ALL T
*/
bool = !leaf(x) || gt == GT_ALL || (gt == GT_VARS && !xml_index(x));
// bool = (!x->xn_index || gt == GT_ALL);
if (bool &&
(prepend = chunk_sprintf(label, "%s%s%s",
prepend,
strlen(prepend)?" ":"",
xml_name(x))) == NULL)
goto done;
xe = NULL;
/* First child is unique, then add that, before looping. */
i = 0;
while ((xe = xml_child_each(x, xe, -1)) != NULL){
/* Dont call this if it is index and there are other following */
if (xml_index(xe) && i < nr-1)
;
else
if (xml2cli(f, xe, prepend, gt, label) < 0)
goto done;
if (xml_index(xe)){ /* assume index is first, otherwise need one more while */
if (gt ==GT_ALL && (prepend = chunk_sprintf(label, "%s %s",
prepend,
xml_name(xe))) == NULL)
goto done;
if ((prepend = chunk_sprintf(label, "%s %s",
prepend,
xml_value(xml_child_i(xe, 0)))) == NULL)
goto done;
}
i++;
}
retval = 0;
done:
return retval;
}
/*! Translate from xml tree to JSON
* XXX ugly code could be cleaned up
*/
int
xml2json1(FILE *f, cxobj *x, int level, int eq, int comma)
{
cxobj *xe = NULL;
cxobj *x1;
int retval = -1;
int level1 = level+1;
int level2 = level+2;
int i;
int n;
int eq1;
switch(xml_type(x)){
case CX_BODY:
fprintf(f, "\"%s\"", xml_value(x));
break;
case CX_ELMNT:
if (eq == 2)
fprintf(f, "%*s", 2*level2, "");
else{
fprintf(f, "%*s", 2*level1, "");
fprintf(f, "\"%s\": ", xml_name(x));
}
if (xml_body(x)!=NULL){
if (eq==1){
fprintf(f, "[\n");
fprintf(f, "%*s", 2*level2, "");
}
}
else {
fprintf(f, "{\n");
}
xe = NULL;
n = xml_child_nr(x);
eq1 = 0;
for (i=0; i<n; i++){
xe = xml_child_i(x, i);
if (xml_body(xe)!=NULL){
if ((x1 = xml_child_i(x, i+1)) != NULL){
if (xml_body(x1) && strcmp(xml_name(xe), xml_name(x1)) == 0){
if (!eq1)
eq1 = 1;
}
else
if (eq1)
eq1 = 2; /* last */
}
}
if (xml2json1(f, xe, level1, eq1, (i+1<n)) < 0)
goto done;
if (xml_body(xe)!=NULL){
if (eq1 == 2){
fprintf(f, "\n");
fprintf(f, "%*s", 2*level2, "");
fprintf(f, "]");
}
if (i+1<n)
fprintf(f, ",");
fprintf(f, "\n");
}
if (eq1==2)
eq1 = 0;
}
if (tleaf(x)){
}
else{
fprintf(f, "%*s}", 2*level1, "");
if (comma)
fprintf(f, ",");
fprintf(f, "\n");
}
break;
default:
break;
}
// fprintf(f, "%*s", 2*level, "");
retval = 0;
done:
return retval;
}
int
xml2json(FILE *f, cxobj *x, int level)
{
int retval = 1;
fprintf(f, "{\n");
if (xml2json1(f, x, level, 0, 0) < 0)
goto done;
fprintf(f, "}\n");
retval = 0;
done:
return retval;
}
/*! Validate a single XML node with yang specification
* - If no value and mandatory flag set in spec, report error.
* - Validate value versus spec, and report error if no match. Currently
* only int ranges and string regexp checked.
* @retval 0 OK
*/
int
xml_yang_validate(cxobj *xt,
yang_stmt *ys0)
{
int retval = -1;
cg_var *cv = NULL;
char *reason;
yang_stmt *yc;
int i;
yang_stmt *ys;
/* if not given by argument (overide) use default link */
ys = ys0?ys0:xml_spec(xt);
switch (ys->ys_keyword){
case Y_LIST:
/* fall thru */
case Y_CONTAINER:
for (i=0; i<ys->ys_len; i++){
yc = ys->ys_stmt[i];
if (yc->ys_keyword != Y_LEAF)
continue;
if (yang_mandatory(yc) && xml_find(xt, yc->ys_argument)==NULL){
clicon_err(OE_CFG, 0,"Missing mandatory variable: %s",
yc->ys_argument);
goto done;
}
}
break;
case Y_LEAF:
/* fall thru */
case Y_LEAF_LIST:
/* validate value against ranges, etc */
if ((cv = cv_dup(ys->ys_cv)) == NULL){
clicon_err(OE_UNIX, errno, "cv_dup");
goto done;
}
if (cv_parse(xml_body(xt), cv) <0){
clicon_err(OE_UNIX, errno, "cv_parse");
goto done;
}
if ((ys_cv_validate(cv, ys, &reason)) != 1){
clicon_err(OE_DB, 0,
"validation of %s failed %s",
ys->ys_argument, reason?reason:"");
if (reason)
free(reason);
goto done;
}
break;
default:
break;
}
retval = 0;
done:
if (cv)
cv_free(cv);
return retval;
}
/*! Translate a single xml node to a cligen variable vector. Note not recursive
* @param[in] xt XML tree containing one top node
* @param[in] ys Yang spec containing type specification of top-node of xt
* @param[out] cvv CLIgen variable vector. Should be freed by cvec_free()
* @retval 0 Everything OK, cvv allocated and set
* @retval -1 Something wrong, clicon_err() called to set error. No cvv returned
* 'Not recursive' means that only one level of XML bodies is translated to cvec:s.
* yang is needed to know which type an xml element has.
* Example:
<a>
<b>23</b>
<c>88</c>
<d>
<e>99</e>
</d>
</a>
--> b:23, c:88
* @see cvec2xml
*/
int
xml2cvec(cxobj *xt,
yang_stmt *yt,
cvec **cvv0)
{
int retval = -1;
cvec *cvv = NULL;
cxobj *xc; /* xml iteration variable */
yang_stmt *ys; /* yang spec */
cg_var *cv;
cg_var *ycv;
char *body;
char *reason = NULL;
int ret;
int i = 0;
int len = 0;
xc = NULL;
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL)
len++;
if ((cvv = cvec_new(len)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_new");
goto err;
}
xc = NULL;
/* Go through all children of the xml tree */
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL){
if ((ys = yang_find_syntax((yang_node*)yt, xml_name(xc))) == NULL){
clicon_debug(1, "%s: yang sanity problem: %s in xml but not present in yang under %s",
__FUNCTION__, xml_name(xc), yt->ys_argument);
continue;
}
if ((ycv = ys->ys_cv) != NULL){
if ((body = xml_body(xc)) != NULL){
/* XXX: cvec_add uses realloc, can we avoid that? */
cv = cvec_i(cvv, i++);
if (cv_cp(cv, ycv) < 0){
clicon_err(OE_PLUGIN, errno, "cv_cp");
goto err;
}
if ((ret = cv_parse1(body, cv, &reason)) < 0){
clicon_err(OE_PLUGIN, errno, "cv_parse");
goto err;
}
if (ret == 0){
clicon_err(OE_PLUGIN, errno, "cv_parse: %s", reason);
if (reason)
free(reason);
goto err;
}
}
}
}
if (debug > 1){
clicon_debug(2, "%s cvv:\n", __FUNCTION__);
cvec_print(stderr, cvv);
}
*cvv0 = cvv;
return 0;
err:
if (cvv)
cvec_free(cvv);
return retval;
}
/*! Translate a cligen variable vector to an XML tree with depth one
* @param[in] cvv CLIgen variable vector. Should be freed by cvec_free()
* @param[in] toptag The XML tree in xt will have this XML tag
* @param[out] xt Pointer to XML tree containing one top node. Should be freed with xml_free
* @retval 0 Everything OK, cvv allocated and set
* @retval -1 Something wrong, clicon_err() called to set error. No xt returned
* @see xml2cvec
* @see cvec2xml This does more but has an internal xml2cvec translation
*/
int
cvec2xml_1(cvec *cvv, char *toptag, cxobj **xt0)
{
int retval = -1;
cxobj *xt = NULL;
cxobj *xn;
cxobj *xb;
cg_var *cv;
char *val;
int len=0;
int i;
cv = NULL;
while ((cv = cvec_each(cvv, cv)) != NULL)
len++;
if ((xt = xml_new(toptag, NULL)) == NULL)
goto err;
if (xml_childvec_set(xt, len) < 0)
goto err;
cv = NULL;
i = 0;
while ((cv = cvec_each(cvv, cv)) != NULL) {
if ((xn = xml_new(cv_name_get(cv), NULL)) == NULL) /* this leaks */
goto err;
xml_parent_set(xn, xt);
xml_child_i_set(xt, i++, xn);
if ((xb = xml_new("body", xn)) == NULL) /* this leaks */
goto err;
xml_type_set(xb, CX_BODY);
val = cv2str_dup(cv);
xml_value_set(xb, val); /* this leaks */
if (val)
free(val);
}
*xt0 = xt;
return 0;
err:
if (xt)
xml_free(xt);
return retval;
}
/*! Return 1 if value is a body of one of the named children of xt */
static int
xml_is_body(cxobj *xt,
char *name,
char *val)
{
cxobj *x;
x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if (strcmp(name, xml_name(x)))
continue;
if (strcmp(xml_body(x), val) == 0)
return 1;
}
return 0;
}
/*! Recursive help function to compute differences between two xml trees
* @see dbdiff_vector.
*/
static int
xml_diff1(yang_stmt *ys,
cxobj *xt1,
cxobj *xt2,
cxobj ***first,
size_t *firstlen,
cxobj ***second,
size_t *secondlen,
cxobj ***changed1,
cxobj ***changed2,
size_t *changedlen)
{
int retval = -1;
cxobj *x1 = NULL;
cxobj *x2 = NULL;
yang_stmt *y;
yang_stmt *ykey;
char *name;
cg_var *cvi;
cvec *cvk = NULL; /* vector of index keys */
char *keyname;
int equal;
char *body1;
char *body2;
clicon_debug(2, "%s: %s", __FUNCTION__, ys->ys_argument?ys->ys_argument:"yspec");
/* Check nodes present in xt1 and xt2 + nodes only in xt1
* Loop over xt1
*/
x1 = NULL;
while ((x1 = xml_child_each(xt1, x1, CX_ELMNT)) != NULL){
name = xml_name(x1);
if (ys->ys_keyword == Y_SPEC)
y = yang_find_topnode((yang_spec*)ys, name);
else
y = yang_find_syntax((yang_node*)ys, name);
if (y == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
goto done;
}
switch (y->ys_keyword){
case Y_LIST:
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, y->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
/* Iterate over xt2 tree to (1) find a child that matches name
(2) that have keys that matches */
equal = 0;
x2 = NULL;
while ((x2 = xml_child_each(xt2, x2, CX_ELMNT)) != NULL){
if (strcmp(xml_name(x2), name))
continue;
cvi = NULL;
equal = 0;
/* (2) Match keys between x1 and x2 */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((body1 = xml_find_body(x1, keyname)) == NULL)
continue; /* may be error */
if ((body2 = xml_find_body(x2, keyname)) == NULL)
continue; /* may be error */
if (strcmp(body1, body2)==0)
equal=1;
else{
equal=0; /* stop as soon as inequal key found */
break;
}
}
if (equal) /* found x1 and x2 equal, otherwise look
for other x2 */
break;
}
if (equal){
if (xml_diff1(y, x1, x2,
first, firstlen,
second, secondlen,
changed1, changed2, changedlen)< 0)
goto done;
break;
}
else
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
case Y_CONTAINER:
/* Equal regardless */
if ((x2 = xml_find(xt2, name)) == NULL){
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
}
if (xml_diff1(y, x1, x2,
first, firstlen,
second, secondlen,
changed1, changed2, changedlen)< 0)
goto done;
break;
case Y_LEAF:
if ((x2 = xml_find(xt2, name)) == NULL){
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
}
if (strcmp(xml_body(x1), xml_body(x2))){
if (cxvec_append(x1, changed1, changedlen) < 0)
goto done;
(*changedlen)--; /* append two vectors */
if (cxvec_append(x2, changed2, changedlen) < 0)
goto done;
}
break;
case Y_LEAF_LIST:
body1 = xml_body(x1);
if (!xml_is_body(xt2, name, body1)) /* where body is */
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
default:
break;
}
} /* while xt1 */
/* Check nodes present only in xt2
* Loop over xt2
*/
x2 = NULL;
while ((x2 = xml_child_each(xt2, x2, CX_ELMNT)) != NULL){
name = xml_name(x2);
if (ys->ys_keyword == Y_SPEC)
y = yang_find_topnode((yang_spec*)ys, name);
else
y = yang_find_syntax((yang_node*)ys, name);
if (y == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
goto done;
}
switch (y->ys_keyword){
case Y_LIST:
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, y->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
/* Iterate over xt1 tree to (1) find a child that matches name
(2) that have keys that matches */
equal = 0;
x1 = NULL;
while ((x1 = xml_child_each(xt1, x1, CX_ELMNT)) != NULL){
if (strcmp(xml_name(x1), name))
continue;
cvi = NULL;
equal = 0;
/* (2) Match keys between x2 and x1 */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((body2 = xml_find_body(x2, keyname)) == NULL)
continue; /* may be error */
if ((body1 = xml_find_body(x1, keyname)) == NULL)
continue; /* may be error */
if (strcmp(body2, body1)==0)
equal=1;
else{
equal=0; /* stop as soon as inequal key found */
break;
}
}
if (equal) /* found x1 and x2 equal, otherwise look
for other x2 */
break;
}
if (!equal)
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
case Y_CONTAINER:
/* Equal regardless */
if ((x1 = xml_find(xt1, name)) == NULL)
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
case Y_LEAF:
if ((x1 = xml_find(xt1, name)) == NULL)
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
case Y_LEAF_LIST:
body2 = xml_body(x2);
if (!xml_is_body(xt1, name, body2)) /* where body is */
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
default:
break;
}
} /* while xt1 */
retval = 0;
done:
return retval;
}
/*! Compute differences between two xml trees
* @param[in] yspec Yang specification
* @param[in] xt1 First XML tree
* @param[in] xt2 Second XML tree
* @param[out] first Pointervector to XML nodes existing in only first tree
* @param[out] firstlen Length of first vector
* @param[out] second Pointervector to XML nodes existing in only second tree
* @param[out] secondlen Length of second vector
* @param[out] changed1 Pointervector to XML nodes changed value
* @param[out] changed2 Pointervector to XML nodes changed value
* @param[out] changedlen Length of changed vector
* All xml vectors should be freed after use.
* Bot xml trees should be freed with xml_free()
*/
int
xml_diff(yang_spec *yspec,
cxobj *xt1,
cxobj *xt2,
cxobj ***first,
size_t *firstlen,
cxobj ***second,
size_t *secondlen,
cxobj ***changed1,
cxobj ***changed2,
size_t *changedlen)
{
int retval = -1;
*firstlen = 0;
*secondlen = 0;
*changedlen = 0;
if (xt1 == NULL && xt2 == NULL)
return 0;
if (xt2 == NULL){
if (cxvec_append(xt1, first, firstlen) < 0)
goto done;
goto ok;
}
if (xt1 == NULL){
if (cxvec_append(xt1, second, secondlen) < 0)
goto done;
goto ok;
}
if (xml_diff1((yang_stmt*)yspec, xt1, xt2,
first, firstlen,
second, secondlen,
changed1, changed2, changedlen) < 0)
goto done;
ok:
retval = 0;
done:
return retval;
}

View file

@ -0,0 +1,50 @@
/*
*
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/>.
* XML parser
*/
#ifndef _CLICON_XML_PARSE_H_
#define _CLICON_XML_PARSE_H_
/*
* Types
*/
struct xml_parse_yacc_arg{
char *ya_parse_string; /* original (copy of) parse string */
int ya_linenum; /* Number of \n in parsed buffer */
void *ya_lexbuf; /* internal parse buffer from lex */
cxobj *ya_xelement; /* xml active element */
cxobj *ya_xparent; /* xml parent element*/
};
extern char *clicon_xml_parsetext;
/*
* Prototypes
*/
int clicon_xml_parsel_init(struct xml_parse_yacc_arg *ya);
int clicon_xml_parsel_exit(struct xml_parse_yacc_arg *ya);
int clicon_xml_parsel_linenr(void);
int clicon_xml_parselex(void *);
int clicon_xml_parseparse(void *);
#endif /* _CLICON_XML_PARSE_H_ */

138
lib/src/clicon_xml_parse.l Normal file
View file

@ -0,0 +1,138 @@
/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
CLIXON 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.
CLIXON 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 CLIXON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
* XML parser
*/
%{
#include "clicon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "clicon_xml_parse.tab.h" /* generated file */
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clicon_xml.h"
#include "clicon_xml_parse.h"
/* Redefine main lex function so that you can send arguments to it: _ya is added to arg list */
#define YY_DECL int clicon_xml_parselex(void *_ya)
/* Dont use input function (use user-buffer) */
#define YY_NO_INPUT
/* typecast macro */
#define _YA ((struct xml_parse_yacc_arg *)_ya)
#undef clicon_xml_parsewrap
int clicon_xml_parsewrap(void)
{
return 1;
}
%}
%x START
%s STATEA
%s CMNT
%s STR
%s TEXTDECL
%s STRDQ
%s STRSQ
%%
<START>[0-9A-Za-z_\-]+ { clicon_xml_parselval.string = strdup(yytext);
return NAME; /* rather be catch-all */
}
<START>[ \t]+ ;
<START>\: return *clicon_xml_parsetext;
<START>\n { _YA->ya_linenum++;}
<START>"<?xml" { BEGIN(TEXTDECL); return BTEXT;}
<START>"/>" { BEGIN(STATEA); return ESLASH; }
<START>"<!--" { BEGIN(CMNT); return BCOMMENT; }
<START>"</" return BSLASH;
<START>[/=] return *clicon_xml_parsetext;
<START>\< return *clicon_xml_parsetext;
<START>\> { BEGIN(STATEA); return *clicon_xml_parsetext; }
<START>\" { BEGIN(STR); return *clicon_xml_parsetext; }
<START>. { clicon_xml_parselval.string = yytext; return CHAR; /*XXX:optimize*/ }
<STATEA>"</" { BEGIN(START); return BSLASH; }
<STATEA>"<!--" { BEGIN(CMNT); return BCOMMENT; }
<STATEA>\< { BEGIN(START); return *clicon_xml_parsetext; }
<STATEA>\n { clicon_xml_parselval.string = yytext;_YA->ya_linenum++; return (CHAR);}
<STATEA>. { clicon_xml_parselval.string = yytext; return CHAR; /*XXX:optimize*/}
<CMNT>"-->" { BEGIN(START); return ECOMMENT; }
<CMNT>\n _YA->ya_linenum++;
<CMNT>.
<TEXTDECL>encoding return ENC;
<TEXTDECL>version return VER;
<TEXTDECL>"=" return *clicon_xml_parsetext;
<TEXTDECL>"?>" { BEGIN(START);return ETEXT;}
<TEXTDECL>\" { BEGIN(STRDQ); return *clicon_xml_parsetext; }
<TEXTDECL>\' { BEGIN(STRSQ); return *clicon_xml_parsetext; }
<STR>[^\"]+ { clicon_xml_parselval.string = strdup(yytext); return CHAR; }
<STR>\" { BEGIN(START); return *clicon_xml_parsetext; }
<STRDQ>1\.[0-9]+ { clicon_xml_parselval.string = strdup(yytext); return CHAR; }
<STRDQ>[^\"]+ { clicon_xml_parselval.string = strdup(yytext); return CHAR; }
<STRDQ>\" { BEGIN(TEXTDECL); return *clicon_xml_parsetext; }
<STRSQ>1\.[0-9]+ { clicon_xml_parselval.string = strdup(yytext); return CHAR; }
<STRSQ>[^\']+ { clicon_xml_parselval.string = strdup(yytext); return CHAR; }
<STRSQ>\' { BEGIN(TEXTDECL); return *clicon_xml_parsetext; }
%%
/*! Initialize XML scanner.
*/
int
clicon_xml_parsel_init(struct xml_parse_yacc_arg *ya)
{
BEGIN(START);
ya->ya_lexbuf = yy_scan_string (ya->ya_parse_string);
if (0)
yyunput(0, ""); /* XXX: just to use unput to avoid warning */
return 0;
}
/*! Exit xml scanner */
int
clicon_xml_parsel_exit(struct xml_parse_yacc_arg *ya)
{
yy_delete_buffer(ya->ya_lexbuf);
#if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9
clicon_xml_parselex_destroy(); /* modern */
#else
yy_init = 1; /* This does not quite free all buffers */
#endif
return 0;
}

344
lib/src/clicon_xml_parse.y Normal file
View file

@ -0,0 +1,344 @@
/*
*
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/>.
* XML parser
*/
%union {
char *string;
}
%start topxml
%token <string> NAME CHAR
%token VER ENC
%token BSLASH ESLASH
%token BTEXT ETEXT
%token BCOMMENT ECOMMENT
%type <string> val aid
%lex-param {void *_ya} /* Add this argument to parse() and lex() function */
%parse-param {void *_ya}
%{
/* typecast macro */
#define _YA ((struct xml_parse_yacc_arg *)_ya)
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clicon_err.h"
#include "clicon_log.h"
#include "clicon_xml.h"
#include "clicon_xml_parse.h"
void
clicon_xml_parseerror(void *_ya, char *s)
{
clicon_err(OE_XML, 0, "xml_parse: line %d: %s: at or before: %s",
_YA->ya_linenum, s, clicon_xml_parsetext);
return;
}
static int
xml_attr_new(struct xml_parse_yacc_arg *ya,
cxobj *xn,
char *name,
char *val)
{
cxobj *xa;
if ((xa = xml_new(name, xn)) == NULL)
return -1;
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, val) < 0)
return -1;
return 0;
}
/* note that we dont handle escaped characters correctly
there may also be some leakage here on NULL return
*/
static int
xml_parse_content(struct xml_parse_yacc_arg *ya, char *str)
{
int sz;
char s0;
cxobj *xn = ya->ya_xelement;
cxobj *xp = ya->ya_xparent;
int retval = -1;
ya->ya_xelement = NULL; /* init */
s0 = str[0];
if (xn != NULL){
sz = strlen(xml_value(xn));
if (s0 == ' ' || s0 == '\n' || s0 == '\t'){
str[0] = ' ';
if (xml_value(xn)[sz-1] == ' ')
goto ok;
}
}
else{
if (s0 == ' ' || s0 == '\n' || s0 == '\t')
goto ok;
if ((xn = xml_new("body", xp)) == NULL)
goto done;
xml_type_set(xn, CX_BODY);
sz = 0;
}
if (xml_value_append(xn, str)==NULL)
goto done;
ya->ya_xelement = xn;
ok:
retval = 0;
done:
return retval;
}
static int
xml_parse_version(struct xml_parse_yacc_arg *ya, char *ver)
{
if(strcmp(ver, "1.0")){
clicon_err(OE_XML, errno, "Wrong XML version %s expected 1.0\n", ver);
free(ver);
return -1;
}
free(ver);
return 0;
}
static int
xml_parse_id(struct xml_parse_yacc_arg *ya, char *name, char *namespace)
{
if ((ya->ya_xelement=xml_new(name, ya->ya_xparent)) == NULL) {
if (namespace)
free(namespace);
free(name);
return -1;
}
xml_namespace_set(ya->ya_xelement, namespace);
if (namespace)
free(namespace);
free(name);
return 0;
}
static int
xml_parse_endslash_pre(struct xml_parse_yacc_arg *ya)
{
ya->ya_xparent = ya->ya_xelement;
ya->ya_xelement = NULL;
return 0;
}
static int
xml_parse_endslash_mid(struct xml_parse_yacc_arg *ya)
{
if (ya->ya_xelement != NULL)
ya->ya_xelement = xml_parent(ya->ya_xelement);
else
ya->ya_xelement = ya->ya_xparent;
ya->ya_xparent = xml_parent(ya->ya_xelement);
return 0;
}
static int
xml_parse_endslash_post(struct xml_parse_yacc_arg *ya)
{
ya->ya_xelement = NULL;
return 0;
}
static int
xml_parse_bslash1(struct xml_parse_yacc_arg *ya, char *name)
{
int retval = -1;
if (strcmp(xml_name(ya->ya_xelement), name)){
clicon_err(OE_XML, 0, "Sanity check failed: %s vs %s",
xml_name(ya->ya_xelement), name);
goto done;
}
if (xml_namespace(ya->ya_xelement)!=NULL){
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s\n",
xml_namespace(ya->ya_xelement), xml_name(ya->ya_xelement), name);
goto done;
}
retval = 0;
done:
free(name);
return retval;
}
static int
xml_parse_bslash2(struct xml_parse_yacc_arg *ya, char *namespace, char *name)
{
int retval = -1;
if (strcmp(xml_name(ya->ya_xelement), name)){
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n",
xml_namespace(ya->ya_xelement),
xml_name(ya->ya_xelement),
namespace,
name);
goto done;
}
if (xml_namespace(ya->ya_xelement)==NULL ||
strcmp(xml_namespace(ya->ya_xelement), namespace)){
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n",
xml_namespace(ya->ya_xelement),
xml_name(ya->ya_xelement),
namespace,
name);
goto done;
}
retval = 0;
done:
free(name);
free(namespace);
return retval;
}
static char*
xml_parse_ida(struct xml_parse_yacc_arg *ya, char *namespace, char *name)
{
char *str;
int len = strlen(namespace)+strlen(name)+2;
if ((str=malloc(len)) == NULL)
return NULL;
snprintf(str, len, "%s:%s", namespace, name);
free(namespace);
free(name);
return str;
}
static int
xml_parse_attr(struct xml_parse_yacc_arg *ya, char *id, char *val)
{
int retval = -1;
if (xml_attr_new(ya, ya->ya_xelement, id, val) < 0)
goto done;
retval = 0;
done:
free(id);
free(val);
return retval;
}
%}
%%
topxml : list
{ clicon_debug(3, "topxml->list ACCEPT");
YYACCEPT; }
| dcl list
{ clicon_debug(3, "topxml->dcl list ACCEPT");
YYACCEPT; }
;
dcl : BTEXT info encode ETEXT { clicon_debug(3, "dcl->info encode"); }
;
info : VER '=' '\"' CHAR '\"'
{ if (xml_parse_version(_YA, $4) <0) YYABORT; }
| VER '=' '\'' CHAR '\''
{ if (xml_parse_version(_YA, $4) <0) YYABORT; }
|
;
encode : ENC '=' '\"' CHAR '\"' {free($4);}
| ENC '=' '\'' CHAR '\'' {free($4);}
;
emnt : '<' id attrs emnt1
{ clicon_debug(3, "emnt -> < id attrs emnt1"); }
;
id : NAME { if (xml_parse_id(_YA, $1, NULL) < 0) YYABORT;
clicon_debug(3, "id -> NAME");}
| NAME ':' NAME { if (xml_parse_id(_YA, $3, $1) < 0) YYABORT;
clicon_debug(3, "id -> NAME : NAME");}
;
emnt1 : ESLASH {_YA->ya_xelement = NULL;
clicon_debug(3, "emnt1 -> />");}
| '>' { xml_parse_endslash_pre(_YA); }
list { xml_parse_endslash_mid(_YA); }
etg { xml_parse_endslash_post(_YA);
clicon_debug(3, "emnt1 -> > list etg");}
;
etg : BSLASH NAME '>'
{ if (xml_parse_bslash1(_YA, $2) < 0) YYABORT;
clicon_debug(3, "etg -> < </ NAME >"); }
| BSLASH NAME ':' NAME '>'
{ if (xml_parse_bslash2(_YA, $2, $4) < 0) YYABORT;
clicon_debug(3, "etg -> < </ NAME:NAME >"); }
;
list : list content { clicon_debug(3, "list -> list content"); }
| content { clicon_debug(3, "list -> content"); }
;
content : emnt { clicon_debug(3, "content -> emnt"); }
| comment { clicon_debug(3, "content -> comment"); }
| CHAR { if (xml_parse_content(_YA, $1) < 0) YYABORT;
clicon_debug(3, "content -> CHAR", $1); }
| { clicon_debug(3, "content -> "); }
;
comment : BCOMMENT ECOMMENT
;
attrs : attrs att
|
;
aid : NAME {$$ = $1;}
| NAME ':' NAME
{ if (($$ = xml_parse_ida(_YA, $1, $3)) == NULL) YYABORT; }
;
att : aid '=' val { if (xml_parse_attr(_YA, $1, $3) < 0) YYABORT; }
;
val : '\"' CHAR '\"' { $$=$2; /* $2 must be consumed */}
| '\"' '\"' { $$=strdup(""); /* $2 must be consumed */}
;
%%

BIN
lib/src/clicon_xpath Normal file

Binary file not shown.

831
lib/src/clicon_xsl.c Normal file
View file

@ -0,0 +1,831 @@
/*
*
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/>.
* Limited XML XPATH and XSLT functions.
* NOTE: there is a main function at the end of this file where you can test out
* different xpath expressions.
*/
/*
Implementation of a limited xslt xpath syntax. Some examples. Given the following
xml tree:
<aaa>
<bbb x="hello"><ccc>42</ccc></bbb>
<bbb x="bye"><ccc>99</ccc></bbb>
<ddd><ccc>22</ccc></ddd>
</aaa>
With the following xpath examples. There are some diffs and many limitations compared
to the xml standards:
/ whole tree <aaa>...</aaa>
/bbb
/aaa/bbb <bbb x="hello"><ccc>42</ccc></bbb>
<bbb x="bye"><ccc>99</ccc></bbb>
//bbb as above
//b?b as above
//b\* as above
//b\*\/ccc <ccc>42</ccc>
<ccc>99</ccc>
//\*\/ccc <ccc>42</ccc>
<ccc>99</ccc>
<ccc>22</ccc>
-- //bbb@x x="hello"
//bbb[@x] <bbb x="hello"><ccc>42</ccc></bbb>
<bbb x="bye"><ccc>99</ccc></bbb>
//bbb[@x=hello] <bbb x="hello"><ccc>42</ccc></bbb>
//bbb[@x="hello"] as above
//bbb[0] <bbb x="hello"><ccc>42</ccc></bbb>
//bbb[ccc=99] <bbb x="bye"><ccc>99</ccc></bbb>
--- //\*\/[ccc=99] same as above
'//bbb | //ddd' <bbb><ccc>42</ccc></bbb>
<bbb x="hello"><ccc>99</ccc></bbb>
<ddd><ccc>22</ccc></ddd> (NB spaces)
etc
For xpath v1.0 see http://www.w3.org/TR/xpath/
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clicon_err.h"
#include "clicon_xml.h"
#include "clicon_xsl.h"
/* Constants */
#define XPATH_VEC_START 128
/*
* Types
*/
struct searchvec{
cxobj **sv_v0; /* here is result */
int sv_v0len;
cxobj **sv_v1; /* this is tmp storage */
int sv_v1len;
int sv_max;
};
typedef struct searchvec searchvec;
/* Local types
*/
enum axis_type{
A_SELF,
A_CHILD,
A_PARENT,
A_ROOT,
A_ANCESTOR,
A_DESCENDANT_OR_SELF, /* actually descendant-or-self */
};
struct map_str2int{
char *ms_str; /* string as in 4.2.4 in RFC 6020 */
int ms_int;
};
/* Mapping between axis type string <--> int */
static const struct map_str2int atmap[] = {
{"self", A_SELF},
{"child", A_CHILD},
{"parent", A_PARENT},
{"root", A_ROOT},
{"ancestor", A_ANCESTOR},
{"descendant-or-self", A_DESCENDANT_OR_SELF},
{NULL, -1}
};
struct xpath_element{
struct xpath_element *xe_next;
enum axis_type xe_type;
char *xe_str; /* eg for child */
char *xe_predicate; /* eg within [] */
};
static int xpath_split(char *xpathstr, char **pathexpr);
static char *axis_type2str(enum axis_type type) __attribute__ ((unused));
static char *
axis_type2str(enum axis_type type)
{
const struct map_str2int *at;
for (at = &atmap[0]; at->ms_str; at++)
if (at->ms_int == type)
return at->ms_str;
return NULL;
}
static int
xpath_print(FILE *f, struct xpath_element *xplist)
{
struct xpath_element *xe;
for (xe=xplist; xe; xe=xe->xe_next)
fprintf(f, "\t:%s %s\n", axis_type2str(xe->xe_type),
xe->xe_str?xe->xe_str:"");
return 0;
}
static int
xpath_element_new(enum axis_type atype,
char *str,
struct xpath_element ***xpnext)
{
int retval = -1;
struct xpath_element *xe;
char *str1 = NULL;
char *pred;
if ((xe = malloc(sizeof(*xe))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(xe, 0, sizeof(*xe));
xe->xe_type = atype;
if (str){
if ((str1 = strdup(str)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
goto done;
}
if (xpath_split(str1, &pred) < 0)
goto done;
if (strlen(str1)){
if ((xe->xe_str = strdup(str1)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
goto done;
}
}
else{
if ((xe->xe_str = strdup("*")) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
goto done;
}
}
if (pred && strlen(pred) && (xe->xe_predicate = strdup(pred)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
goto done;
}
}
(**xpnext) = xe;
*xpnext = &xe->xe_next;
retval = 0;
done:
if (str1)
free(str1);
return retval;
}
static int
xpath_element_free(struct xpath_element *xe)
{
if (xe->xe_str)
free(xe->xe_str);
if (xe->xe_predicate)
free(xe->xe_predicate);
free(xe);
return 0;
}
static int
xpath_free(struct xpath_element *xplist)
{
struct xpath_element *xe, *xe_next;
for (xe=xplist; xe; xe=xe_next){
xe_next = xe->xe_next;
xpath_element_free(xe);
}
return 0;
}
/*
* // is short for /descendant-or-self::node()/
*/
static int
xpath_parse(char *xpath, struct xpath_element **xplist0)
{
int retval = -1;
int nvec = 0;
char *p;
char *s;
char *s0;
int i;
struct xpath_element *xplist = NULL;
struct xpath_element **xpnext = &xplist;
if ((s0 = strdup(xpath)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
goto done;
}
s = s0;
if (strlen(s))
nvec = 1;
while ((p = index(s, '/')) != NULL){
nvec++;
*p = '\0';
s = p+1;
}
s = s0;
for (i=0; i<nvec; i++){
if ((i==0 && strcmp(s,"")==0)) /* Initial / or // */
xpath_element_new(A_ROOT, NULL, &xpnext);
else if (i!=nvec-1 && strcmp(s,"")==0)
xpath_element_new(A_DESCENDANT_OR_SELF, NULL, &xpnext);
else if (strncmp(s,"descendant-or-self::", strlen("descendant-or-self::"))==0){
xpath_element_new(A_DESCENDANT_OR_SELF, s+strlen("descendant-or-self::"), &xpnext);
}
else if (strncmp(s,".", strlen("."))==0)
xpath_element_new(A_SELF, s+strlen("."), &xpnext);
else if (strncmp(s,"self::", strlen("self::"))==0)
xpath_element_new(A_SELF, s+strlen("self::"), &xpnext);
else if (strncmp(s,"..", strlen(".."))==0)
xpath_element_new(A_PARENT, s+strlen(".."), &xpnext);
else if (strncmp(s,"parent::", strlen("parent::"))==0)
xpath_element_new(A_PARENT, s+strlen("parent::"), &xpnext);
else if (strncmp(s,"ancestor::", strlen("ancestor::"))==0)
xpath_element_new(A_ANCESTOR, s+strlen("ancestor::"), &xpnext);
else if (strncmp(s,"child::", strlen("child::"))==0)
xpath_element_new(A_CHILD, s+strlen("child::"), &xpnext);
else
xpath_element_new(A_CHILD, s, &xpnext);
s += strlen(s) + 1;
}
retval = 0;
done:
if (s0)
free(s0);
if (retval == 0)
*xplist0 = xplist;
return retval;
}
/*! Find a node 'deep' in an XML tree
*
* The xv_* arguments are filled in nodes found earlier.
* args:
* @param[in] xn_parent Base XML object
* @param[in] name shell wildcard pattern to match with node name
* @param[in] node_type CX_ELMNT, CX_ATTR or CX_BODY
* @param[in,out] vec1 internal buffers with results
* @param[in,out] vec0 internal buffers with results
* @param[in,out] vec_len internal buffers with length of vec0,vec1
* @param[in,out] vec_max internal buffers with max of vec0,vec1
* returns:
* 0 on OK, -1 on error
*/
static int
recursive_find(cxobj *xn,
char *pattern,
int node_type,
cxobj ***vec0,
size_t *vec0len)
{
int retval = -1;
cxobj *xsub;
cxobj **vec = *vec0;
size_t veclen = *vec0len;
xsub = NULL;
while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) {
if (fnmatch(pattern, xml_name(xsub), 0) == 0){
if (cxvec_append(xsub, &vec, &veclen) < 0)
goto done;
// continue; /* Dont go deeper */
}
if (recursive_find(xsub, pattern, node_type, &vec, &veclen) < 0)
goto done;
}
retval = 0;
*vec0 = vec;
*vec0len = veclen;
done:
return retval;
}
static int
xpath_expr(char *e,
cxobj ***vec0,
size_t *vec0len)
{
char *e_a;
char *e_v;
int i;
int retval = -1;
cxobj *x;
cxobj *xv;
cxobj **vec = NULL;
size_t veclen = 0;
int oplen;
char *tag;
char *val;
if (*e == '@'){ /* @ attribute */
e++;
e_v=e;
e_a = strsep(&e_v, "=");
if (e_a == NULL){
clicon_err(OE_XML, errno, "%s: malformed expression: [@%s]",
__FUNCTION__, e);
goto done;
}
for (i=0; i<*vec0len; i++){
xv = (*vec0)[i];
if ((x = xml_find(xv, e_a)) != NULL &&
(xml_type(x) == CX_ATTR)){
if (!e_v || strcmp(xml_value(x), e_v) == 0)
if (cxvec_append(xv, &vec, &veclen) < 0)
goto done;
}
}
}
else{ /* either <n> or <tag><op><value>, where <op>='=' for now */
oplen = strcspn(e, "=");
if (strlen(e+oplen)==0){ /* no operator */
if (sscanf(e, "%d", &i) == 1){ /* number */
if (i < *vec0len){
xv = (*vec0)[i]; /* XXX: cant compress: gcc breaks */
if (cxvec_append(xv, &vec, &veclen) < 0)
goto done;
}
}
else{
clicon_err(OE_XML, errno, "%s: malformed expression: [%s]",
__FUNCTION__, e);
goto done;
}
}
else{
if ((tag = strsep(&e, "=")) == NULL){
clicon_err(OE_XML, errno, "%s: malformed expression: [%s]",
__FUNCTION__, e);
goto done;
}
for (i=0; i<*vec0len; i++){
xv = (*vec0)[i];
if ((x = xml_find(xv, tag)) != NULL &&
(xml_type(x) == CX_ELMNT)){
if ((val = xml_body(x)) != NULL &&
strcmp(val, e) == 0){
if (cxvec_append(xv, &vec, &veclen) < 0)
goto done;
}
}
}
}
}
/* copy the array from 1 to 0 */
free(*vec0);
*vec0 = vec;
*vec0len = veclen;
retval = 0;
done:
return retval;
}
/*! Given vec0, add matches to vec1
* @param[in] xe
* @param[in] descendants0
* @param[in] vec0
* @param[in] vec0len
* @param[out] vec1
* @param[out] vec1len
* XXX: Kommer in i funktionen med vec0, resultatet appendas i vec1
* vec0 --> vec
* Det är nog bra om vec0 inte ändras, är input parameter
* Vid utgång ska vec1 innehålla resultatet.
* Internt ?
* XXX: hantering av (input)vec0-->vec-->vec2-->vec1 (resultat)
*/
static int
xpath_find(struct xpath_element *xe,
int descendants0,
cxobj **vec0,
size_t vec0len,
cxobj ***vec2,
size_t *vec2len
)
{
int retval = -1;
int i;
int j;
cxobj *x = NULL;
cxobj *xv;
int descendants = 0;
cxobj **vec1 = NULL;
size_t vec1len = 0;
if (xe == NULL){
// append
for (i=0; i<vec0len; i++){
xv = vec0[i];
cxvec_append(xv, vec2, vec2len);
}
free(vec0);
return 0;
}
#if 0
fprintf(stderr, "%s: %s: \"%s\"\n", __FUNCTION__,
axis_type2str(xe->xe_type), xe->xe_str?xe->xe_str:"");
#endif
switch (xe->xe_type){
case A_SELF:
break;
case A_PARENT:
for (i=0; i<vec0len; i++){
xv = vec0[i];
vec0[i] = xml_parent(xv);
}
break;
case A_ROOT: /* set list to NULL */
x = vec0[0];
assert(x != NULL);
while (xml_parent(x) != NULL)
x = xml_parent(x);
free(vec0);
if ((vec0 = calloc(1, sizeof(cxobj *))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
vec0[0] = x;
vec0len = 1;
break;
case A_CHILD:
if (descendants0){
for (i=0; i<vec0len; i++){
xv = vec0[i];
if (recursive_find(xv, xe->xe_str, CX_ELMNT, &vec1, &vec1len) < 0)
goto done;
}
}
else
for (i=0; i<vec0len; i++){
xv = vec0[i];
x = NULL;
while ((x = xml_child_each(xv, x, -1)) != NULL) {
if (fnmatch(xe->xe_str, xml_name(x), 0) == 0){
if (cxvec_append(x, &vec1, &vec1len) < 0)
goto done;
}
}
}
free(vec0);
vec0 = vec1;
vec0len = vec1len;
break;
case A_DESCENDANT_OR_SELF:
/* Instead of collecting all descendants (which we could)
just set a flag and treat that in the next operation */
descendants++;
break;
default:
break;
}
/* remove duplicates */
for (i=0; i<vec0len; i++){
for (j=i+1; j<vec0len; j++){
if (vec0[i] == vec0[j]){
memmove(vec0[j], vec0[j+1], (vec0len-j)*sizeof(cxobj*));
vec0len--;
}
}
}
if (xe->xe_predicate)
if (xpath_expr(xe->xe_predicate, &vec0, &vec0len) < 0)
goto done;
if (xpath_find(xe->xe_next, descendants,
vec0, vec0len,
vec2, vec2len) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Transform eg "a/b[kalle]" -> "a/b" e="kalle" */
static int
xpath_split(char *xpathstr, char **pathexpr)
{
int retval = -1;
int last;
int i;
char *pe = NULL;
if (strlen(xpathstr)){
last = strlen(xpathstr) - 1; /* XXX: this could be -1.. */
if (xpathstr[last] == ']'){
xpathstr[last] = '\0';
if (strlen(xpathstr)){
last = strlen(xpathstr) - 1; /* recompute due to null */
for (i=last; i>=0; i--){
if (xpathstr[i] == '['){
xpathstr[i] = '\0';
pe = &xpathstr[i+1];
break;
}
}
if (pe==NULL){
clicon_err(OE_XML, errno, "%s: mismatched []: %s", __FUNCTION__, xpathstr);
goto done;
}
}
}
}
retval = 0;
done:
*pathexpr = pe;
return retval;
}
/*! Process single xpath expression on xml tree
* @param[in] xpath
* @param[in] vec0
* @param[in] vec0len
* @param[out] vec
* @param[out] veclen
*/
static int
xpath_exec(char *xpath,
cxobj **vec0,
size_t vec0len,
cxobj ***vec2,
size_t *vec2len)
{
struct xpath_element *xplist;
cxobj **vec1;
size_t vec1len;
if (cxvec_dup(vec0, vec0len, &vec1, &vec1len) < 0)
goto done;
if (xpath_parse(xpath, &xplist) < 0)
goto done;
if (0)
xpath_print(stderr, xplist);
if (xpath_find(xplist, 0, vec1, vec1len, vec2, vec2len) < 0)
goto done;
if (xpath_free(xplist) < 0)
goto done;
done:
return 0;
} /* xpath_exec */
/*! Intermediate xpath function to handle 'conditional' cases.
* For example: xpath = //a | //b.
* xpath_first+ splits xpath up in several subcalls
* (eg xpath=//a and xpath=//b) and collects the results.
* Note: if a match is found in both, two (or more) same results will be
* returned.
* Note, this could be 'folded' into xpath1 but I judged it too complex.
*/
static int
xpath_choice(cxobj *xtop,
char *xpath0,
cxobj ***vec1,
size_t *vec1len)
{
int retval = -1;
char *s0;
char *s1;
char *s2;
char *xpath;
cxobj **vec0 = NULL;
size_t vec0len = 0;
if ((s0 = strdup(xpath0)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
goto done;
}
s2 = s1 = s0;
if ((vec0 = calloc(1, sizeof(cxobj *))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
vec0[0] = xtop;
vec0len++;
while (s1 != NULL){
s2 = strstr(s1, " | ");
if (s2 != NULL){
*s2 = '\0'; /* terminate xpath */
s2 += 3;
}
xpath = s1;
s1 = s2;
if (xpath_exec(xpath, vec0, vec0len, vec1, vec1len) < 0)
goto done;
}
retval = 0;
done:
if (s0)
free(s0);
if (vec0)
free(vec0);
return retval;
}
/*! A restricted xpath function where the first matching entry is returned
* See xpath1() on details for subset.
* args:
* @param[in] cxtop xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @retval xml-tree of first match, or NULL on error.
*
* @code
* cxobj *x;
* if ((x = xpath_first(xtop, "//symbol/foo")) != NULL) {
* ...
* }
* @endcode
* Note that the returned pointer points into the original tree so should not be freed
* after use.
* @see also xpath_vec.
*/
cxobj *
xpath_first(cxobj *cxtop, char *xpath)
{
cxobj **vec0 = NULL;
size_t vec0len = 0;
cxobj *xn = NULL;
if (xpath_choice(cxtop, xpath, &vec0, &vec0len) < 0)
goto done;
if (vec0len)
xn = vec0[0];
else
xn = NULL;
done:
if (vec0)
free(vec0);
return xn;
}
/*! A restricted xpath iterator that loops over all matching entries. Dont use.
*
* See xpath1() on details for subset.
* @param[in] cxtop xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @param[in] xprev iterator/result should be initiated to NULL
* @retval xml-tree of n:th match, or NULL on error.
*
* @code
* cxobj *x = NULL;
* while ((x = xpath_each(cxtop, "//symbol/foo", x)) != NULL) {
* ...
* }
* @endcode
*
* Note that the returned pointer points into the original tree so should not be freed
* after use.
* @see also xpath, xpath_vec.
* NOTE: uses a static variable: consider replacing with xpath_vec() instead
*/
cxobj *
xpath_each(cxobj *cxtop, char *xpath, cxobj *xprev)
{
static cxobj **vec0 = NULL; /* XXX */
static size_t vec0len = 0;
cxobj *xn = NULL;
int i;
if (xprev == NULL){
if (vec0) // XXX
free(vec0); // XXX
vec0len = 0;
if (xpath_choice(cxtop, xpath, &vec0, &vec0len) < 0)
goto done;
}
if (vec0len){
if (xprev==NULL)
xn = vec0[0];
else{
for (i=0; i<vec0len; i++)
if (vec0[i] == xprev)
break;
if (i>=vec0len-1)
xn = NULL;
else
xn = vec0[i+1];
}
}
else
xn = NULL;
done:
return xn;
}
/*! A restricted xpath that returns a vector of matches
*
* See xpath1() on details for subset.
* @param[in] cxtop xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @param[out] xv_len returns length of vector in return value
* @retval vec vector of xml-trees. Vector must be free():d after use
* @retval NULL NULL on error.
*
* @code
* cxobj **xv;
* int xlen;
* if ((xv = xpath_vec(cxtop, "//symbol/foo", &xlen)) != NULL) {
* for (i=0; i<xlen; i++){
* xn = xv[i];
* ...
* }
* free(xv);
* }
* @endcode
* Note that although the returned vector must be freed after use, the returned xml
* trees need not be.
* @see also xpath_first, xpath_each.
*/
cxobj **
xpath_vec(cxobj *cxtop,
char *xpath,
int *veclen)
{
cxobj **vec=NULL;
*veclen = 0;
if (xpath_choice(cxtop, xpath, &vec, (size_t*)veclen) < 0)
return NULL;
return vec;
}
/*
* Turn this on to get an xpath test program
* Usage: clicon_xpath [<xpath>]
* read xml from input
* Example compile:
gcc -g -o xpath -I. -I../clicon ./clicon_xsl.c -lclicon -lcligen
*/
#if 0 /* Test program */
static int
usage(char *argv0)
{
fprintf(stderr, "usage:%s <xpath>.\n\tInput on stdin\n", argv0);
exit(0);
}
int
main(int argc, char **argv)
{
int i;
cxobj **xv;
cxobj *x;
cxobj *xn;
int xlen = 0;
if (argc != 2){
usage(argv[0]);
return 0;
}
if (clicon_xml_parse_file(0, &x, "</clicon>") < 0){
fprintf(stderr, "parsing 2\n");
return -1;
}
printf("\n");
if ((xv = xpath_vec(x, argv[1], &xlen)) != NULL) {
for (i=0; i<xlen; i++){
xn = xv[i];
fprintf(stdout, "[%d]:\n", i);
clicon_xml2file(stdout, xn, 0, 1);
}
free(xv);
}
if (x)
xml_free(x);
return 0;
}
#endif /* Test program */

2034
lib/src/clicon_yang.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
/*
*
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/>.
* Database specification parser cli syntax
* (Cloned from cligen parser)
*/
#ifndef _CLICON_YANG_PARSE_H_
#define _CLICON_YANG_PARSE_H_
/*
* Types
*/
struct ys_stack{
struct ys_stack *ys_next;
struct yang_node *ys_node;
};
struct clicon_yang_yacc_arg{ /* XXX: mostly unrelevant */
clicon_handle yy_handle; /* cligen_handle */
char *yy_name; /* Name of syntax (for error string) */
int yy_linenum; /* Number of \n in parsed buffer */
char *yy_parse_string; /* original (copy of) parse string */
void *yy_lexbuf; /* internal parse buffer from lex */
struct ys_stack *yy_stack; /* Stack of levels: push/pop on () and [] */
int yy_lex_state; /* lex start condition (ESCAPE/COMMENT) */
int yy_lex_string_state; /* lex start condition (STRING) */
yang_stmt *yy_module; /* top-level (sub)module - return value of
parser */
};
/* This is a malloced piece of code we attach to cligen objects used as db-specs.
* So if(when) we translate cg_obj to yang_obj (or something). These are the fields
* we should add.
*/
struct yang_userdata{
char *du_indexvar; /* (clicon) This command is a list and
this string is the key/index of the list
*/
char *du_yang; /* (clicon) Save yang key for cli
generation */
int du_optional; /* (clicon) Optional element in list */
struct cg_var *du_default; /* default value(clicon) */
char du_vector; /* (clicon) Possibly more than one element */
};
/*
* Variables
*/
extern char *clicon_yang_parsetext;
/*
* Prototypes
*/
int yang_scan_init(struct clicon_yang_yacc_arg *ya);
int yang_scan_exit(struct clicon_yang_yacc_arg *ya);
int yang_parse_init(struct clicon_yang_yacc_arg *ya, yang_spec *ysp);
int yang_parse_exit(struct clicon_yang_yacc_arg *ya);
int clicon_yang_parselex(void *_ya);
int clicon_yang_parseparse(void *);
void clicon_yang_parseerror(void *_ya, char*);
int ystack_pop(struct clicon_yang_yacc_arg *ya);
struct ys_stack *ystack_push(struct clicon_yang_yacc_arg *ya, yang_node *yn);
#endif /* _CLICON_YANG_PARSE_H_ */

260
lib/src/clicon_yang_parse.l Normal file
View file

@ -0,0 +1,260 @@
/*
*
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/>.
* Database specification parser cli syntax
* (Cloned from cligen parser)
*/
%{
#include "clicon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <netinet/in.h>
#include "clicon_yang_parse.tab.h" /* generated */
#include <cligen/cligen.h>
#include "clicon_queue.h"
#include "clicon_hash.h"
#include "clicon_handle.h"
#include "clicon_yang.h"
#include "clicon_yang_parse.h"
/* Redefine main lex function so that you can send arguments to it: _yy is added to arg list */
#define YY_DECL int clicon_yang_parselex(void *_yy)
/* Dont use input function (use user-buffer) */
#define YY_NO_INPUT
/* typecast macro */
#define _YY ((struct clicon_yang_yacc_arg *)_yy)
#define MAXBUF 4*4*64*1024
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
#undef clicon_yang_parsewrap
int
clicon_yang_parsewrap(void)
{
return 1;
}
#ifdef notused
/* like strdup but strip \:s */
static char *
stripdup(char *s0)
{
char *s1;
char *s;
if ((s1 = strdup(s0)) == NULL){
fprintf(stderr, "%s: strdup: %s\n", __FUNCTION__, strerror(errno));
return NULL;
}
while ((s = index(s1, '\\')) != NULL)
memmove(s, s+1, strlen(s));
return s1;
}
#endif /* notused */
/*
statement = keyword [argument] (";" / "{" *statement "}")
The argument is a string
Example: keyword argument ; keyword ; keyword { keyword argument; } keyword
STRING0 corresponds to string rule
ARGUMENT corresponds to identifier-arg-str,unique-arg-str,key-arg-str, etc.
more to do here.
*/
%}
%x KEYWORD
%s ARGUMENT
%s STRING0
%s STRING1
%s STRING2
%s ESCAPE
%s COMMENT1
%s COMMENT2
%%
/* Common tokens */
<KEYWORD,ARGUMENT,STRING0>[ \t]
<KEYWORD,ARGUMENT,STRING0><<EOF>> { return MY_EOF; }
<KEYWORD,ARGUMENT,STRING0,COMMENT1>\n { _YY->yy_linenum++; }
<KEYWORD,ARGUMENT,STRING0>"/*" { _YY->yy_lex_state = YYSTATE; BEGIN(COMMENT1); }
<KEYWORD,ARGUMENT,STRING0>"//" { _YY->yy_lex_state = YYSTATE; BEGIN(COMMENT2); }
<KEYWORD>\} { return *yytext; }
/* RFC 6020 keywords */
<KEYWORD>anyxml { BEGIN(ARGUMENT); return K_ANYXML; }
<KEYWORD>argument { BEGIN(ARGUMENT); return K_ARGUMENT; }
<KEYWORD>augment { BEGIN(ARGUMENT); return K_AUGMENT; }
<KEYWORD>base { BEGIN(ARGUMENT); return K_BASE; }
<KEYWORD>belongs-to { BEGIN(ARGUMENT); return K_BELONGS_TO; }
<KEYWORD>bit { BEGIN(ARGUMENT); return K_BIT; }
<KEYWORD>case { BEGIN(ARGUMENT); return K_CASE; }
<KEYWORD>choice { BEGIN(ARGUMENT); return K_CHOICE; }
<KEYWORD>config { BEGIN(ARGUMENT); return K_CONFIG; }
<KEYWORD>contact { BEGIN(STRING0); return K_CONTACT; }
<KEYWORD>container { BEGIN(ARGUMENT); return K_CONTAINER; }
<KEYWORD>default { BEGIN(STRING0); return K_DEFAULT; }
<KEYWORD>description { BEGIN(STRING0); return K_DESCRIPTION; }
<KEYWORD>deviate { BEGIN(ARGUMENT); return K_DEVIATE; }
<KEYWORD>deviation { BEGIN(ARGUMENT); return K_DEVIATION; }
<KEYWORD>enum { BEGIN(STRING0); return K_ENUM; }
<KEYWORD>error-app-tag { BEGIN(STRING0); return K_ERROR_APP_TAG; }
<KEYWORD>error-message { BEGIN(STRING0); return K_ERROR_MESSAGE; }
<KEYWORD>extension { BEGIN(ARGUMENT); return K_EXTENSION; }
<KEYWORD>feature { BEGIN(ARGUMENT); return K_FEATURE; }
<KEYWORD>fraction-digits { BEGIN(ARGUMENT); return K_FRACTION_DIGITS; }
<KEYWORD>grouping { BEGIN(ARGUMENT); return K_GROUPING; }
<KEYWORD>identity { BEGIN(ARGUMENT); return K_IDENTITY; }
<KEYWORD>if-feature { BEGIN(ARGUMENT); return K_IF_FEATURE; }
<KEYWORD>import { BEGIN(ARGUMENT); return K_IMPORT; }
<KEYWORD>include { BEGIN(ARGUMENT); return K_INCLUDE; }
<KEYWORD>input { BEGIN(ARGUMENT); return K_INPUT; }
<KEYWORD>key { BEGIN(ARGUMENT); return K_KEY; }
<KEYWORD>leaf { BEGIN(ARGUMENT); return K_LEAF; }
<KEYWORD>leaf-list { BEGIN(ARGUMENT); return K_LEAF_LIST; }
<KEYWORD>length { BEGIN(ARGUMENT); return K_LENGTH; }
<KEYWORD>list { BEGIN(ARGUMENT); return K_LIST; }
<KEYWORD>mandatory { BEGIN(ARGUMENT); return K_MANDATORY; }
<KEYWORD>max-elements { BEGIN(ARGUMENT); return K_MAX_ELEMENTS; }
<KEYWORD>min-elements { BEGIN(ARGUMENT); return K_MIN_ELEMENTS; }
<KEYWORD>module { BEGIN(ARGUMENT); return K_MODULE; }
<KEYWORD>must { BEGIN(STRING0); return K_MUST; }
<KEYWORD>namespace { BEGIN(ARGUMENT); return K_NAMESPACE; }
<KEYWORD>notification { BEGIN(ARGUMENT); return K_NOTIFICATION; }
<KEYWORD>ordered-by { BEGIN(ARGUMENT); return K_ORDERED_BY; }
<KEYWORD>organization { BEGIN(STRING0); return K_ORGANIZATION; }
<KEYWORD>output { BEGIN(ARGUMENT); return K_OUTPUT; }
<KEYWORD>path { BEGIN(ARGUMENT); return K_PATH; }
<KEYWORD>pattern { BEGIN(STRING0); return K_PATTERN; }
<KEYWORD>position { BEGIN(ARGUMENT); return K_POSITION; }
<KEYWORD>prefix { BEGIN(ARGUMENT); return K_PREFIX; }
<KEYWORD>presence { BEGIN(STRING0); return K_PRESENCE; }
<KEYWORD>range { BEGIN(ARGUMENT); return K_RANGE; }
<KEYWORD>reference { BEGIN(STRING0); return K_REFERENCE; }
<KEYWORD>refine { BEGIN(ARGUMENT); return K_REFINE; }
<KEYWORD>require-instance { BEGIN(ARGUMENT); return K_REQUIRE_INSTANCE; }
<KEYWORD>revision { BEGIN(ARGUMENT); return K_REVISION; }
<KEYWORD>revision-date { BEGIN(ARGUMENT); return K_REVISION_DATE; }
<KEYWORD>rpc { BEGIN(ARGUMENT); return K_RPC; }
<KEYWORD>status { BEGIN(ARGUMENT); return K_STATUS; }
<KEYWORD>submodule { BEGIN(ARGUMENT); return K_SUBMODULE; }
<KEYWORD>type { BEGIN(ARGUMENT); return K_TYPE; }
<KEYWORD>typedef { BEGIN(ARGUMENT); return K_TYPEDEF; }
<KEYWORD>unique { BEGIN(ARGUMENT); return K_UNIQUE; }
<KEYWORD>units { BEGIN(STRING0); return K_UNITS; }
<KEYWORD>uses { BEGIN(ARGUMENT); return K_USES; }
<KEYWORD>value { BEGIN(ARGUMENT); return K_VALUE; }
<KEYWORD>when { BEGIN(STRING0); return K_WHEN; }
<KEYWORD>yang-version { BEGIN(ARGUMENT); return K_YANG_VERSION; }
<KEYWORD>yin-element { BEGIN(ARGUMENT); return K_YIN_ELEMENT; }
<KEYWORD>. { return K_UNKNOWN; }
<ARGUMENT>; { BEGIN(KEYWORD); return *yytext; }
<ARGUMENT>\{ { BEGIN(KEYWORD); return *yytext; }
<ARGUMENT>\" { _YY->yy_lex_string_state =ARGUMENT; BEGIN(STRING1); return DQ; }
<ARGUMENT>\' { _YY->yy_lex_string_state =ARGUMENT; BEGIN(STRING2); return DQ; }
<ARGUMENT>\+ { return *yytext; /* many arg rules dont like this */ }
<ARGUMENT>: { return *yytext; /* many arg rules dont like this */ }
<ARGUMENT>. { clicon_yang_parselval.string = strdup(yytext);
return CHAR;}
<STRING0>\{ { BEGIN(KEYWORD); return *yytext; }
<STRING0>; { BEGIN(KEYWORD); return *yytext; }
<STRING0>\" { _YY->yy_lex_string_state =STRING0; BEGIN(STRING1); return DQ; }
<STRING0>\' { _YY->yy_lex_string_state =STRING0; BEGIN(STRING2); return DQ; }
<STRING0>\+ { return *yytext; }
<STRING0>. { clicon_yang_parselval.string = strdup(yytext);
return CHAR;}
<STRING1>\\ { _YY->yy_lex_state = STRING1; BEGIN(ESCAPE); }
<STRING1>\" { BEGIN(_YY->yy_lex_string_state); return DQ; }
<STRING1>\n { _YY->yy_linenum++; clicon_yang_parselval.string = strdup(yytext); return CHAR;}
<STRING1>. { clicon_yang_parselval.string = strdup(yytext);
return CHAR;}
<STRING2>\\ { _YY->yy_lex_state = STRING2; BEGIN(ESCAPE); }
<STRING2>\' { BEGIN(_YY->yy_lex_string_state); return DQ; }
<STRING2>\n { _YY->yy_linenum++; clicon_yang_parselval.string = strdup(yytext); return CHAR;}
<STRING2>. { clicon_yang_parselval.string = strdup(yytext);
return CHAR;}
<ESCAPE>. { BEGIN(_YY->yy_lex_state);
clicon_yang_parselval.string = strdup(yytext);
return CHAR; }
<COMMENT1>[^*\n]* /* eat anything that's not a '*' */
<COMMENT1>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
<COMMENT1>"*"+"/" BEGIN(_YY->yy_lex_state);
<COMMENT2>[^\n]* /* eat anything that's not a '/' */
<COMMENT2>\n { _YY->yy_linenum++; BEGIN(_YY->yy_lex_state); }
%%
/*
* yang_parse_init
* Initialize scanner.
*/
int
yang_scan_init(struct clicon_yang_yacc_arg *yy)
{
BEGIN(KEYWORD);
yy->yy_lexbuf = yy_scan_string (yy->yy_parse_string);
#if 1 /* XXX: just to use unput to avoid warning */
if (0)
yyunput(0, "");
#endif
return 0;
}
/*
* yang_parse_exit
* free buffers
* Even within Flex version 2.5 (this is assumed), freeing buffers is different.
*/
int
yang_scan_exit(struct clicon_yang_yacc_arg *yy)
{
yy_delete_buffer(yy->yy_lexbuf);
#if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9
clicon_yang_parselex_destroy(); /* modern */
#else
yy_init = 1; /* This does not quite free all buffers */
#endif
return 0;
}

1319
lib/src/clicon_yang_parse.y Normal file

File diff suppressed because it is too large Load diff

910
lib/src/clicon_yang_type.c Normal file
View file

@ -0,0 +1,910 @@
/*
*
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/>.
* Yang type related functions
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <ctype.h>
#define __USE_GNU /* strverscmp */
#include <string.h>
#include <arpa/inet.h>
#include <regex.h>
#include <syslog.h>
#include <assert.h>
#include <netinet/in.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clicon_log.h"
#include "clicon_err.h"
#include "clicon_string.h"
#include "clicon_queue.h"
#include "clicon_hash.h"
#include "clicon_handle.h"
#include "clicon_yang.h"
#include "clicon_hash.h"
#include "clicon_chunk.h"
#include "clicon_options.h"
#include "clicon_yang.h"
#include "clicon_yang_type.h"
/*
* Local types and variables
*/
/* Struct used to map between int and strings. Used for:
* - mapping yang types/typedefs (strings) and cligen types (ints).
* - mapping yang keywords (strings) and enum (clicon)
* (same struct in clicon_yang.c)
*/
struct map_str2int{
char *ms_str; /* string as in 4.2.4 in RFC 6020 */
int ms_int;
};
/* Mapping between yang types <--> cligen types
Note, first match used wne translating from cv to yang --> order is significant */
static const struct map_str2int ytmap[] = {
{"int32", CGV_INT32}, /* NOTE, first match on right is significant, dont move */
{"string", CGV_STRING}, /* NOTE, first match on right is significant, dont move */
{"string", CGV_REST}, /* For cv -> yang translation of rest */
{"binary", CGV_STRING},
{"bits", CGV_STRING},
{"boolean", CGV_BOOL},
{"decimal64", CGV_DEC64},
{"empty", CGV_VOID}, /* May not include any content */
{"enumeration", CGV_STRING},
{"identityref", CGV_STRING}, /* XXX */
{"instance-identifier", CGV_STRING}, /* XXX */
{"int8", CGV_INT8},
{"int16", CGV_INT16},
{"int64", CGV_INT64},
{"leafref", CGV_STRING}, /* XXX */
{"uint8", CGV_UINT8},
{"uint16", CGV_UINT16},
{"uint32", CGV_UINT32},
{"uint64", CGV_UINT64},
{"union", CGV_VOID}, /* Is replaced by actual type */
{NULL, -1}
};
int
yang_type_cache_set(yang_type_cache **ycache0,
yang_stmt *resolved,
int options,
cg_var *mincv,
cg_var *maxcv,
char *pattern,
uint8_t fraction)
{
int retval = -1;
yang_type_cache *ycache = *ycache0;
assert (ycache == NULL);
if ((ycache = (yang_type_cache *)malloc(sizeof(*ycache))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(ycache, 0, sizeof(*ycache));
*ycache0 = ycache;
ycache->yc_resolved = resolved;
ycache->yc_options = options;
if (mincv && (ycache->yc_mincv = cv_dup(mincv)) == NULL){
clicon_err(OE_UNIX, errno, "cv_dup");
goto done;
}
if (maxcv && (ycache->yc_maxcv = cv_dup(maxcv)) == NULL){
clicon_err(OE_UNIX, errno, "cv_dup");
goto done;
}
if (pattern && (ycache->yc_pattern = strdup(pattern)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
ycache->yc_fraction = fraction;
retval = 0;
done:
return retval;
}
/*! Get individual fields (direct/destrucively) from yang type cache. */
int
yang_type_cache_get(yang_type_cache *ycache,
yang_stmt **resolved,
int *options,
cg_var **mincv,
cg_var **maxcv,
char **pattern,
uint8_t *fraction)
{
if (resolved)
*resolved = ycache->yc_resolved;
if (options)
*options = ycache->yc_options;
if (mincv)
*mincv = ycache->yc_mincv;
if (maxcv)
*maxcv = ycache->yc_maxcv;
if (pattern)
*pattern = ycache->yc_pattern;
if (fraction)
*fraction = ycache->yc_fraction;
return 0;
}
int
yang_type_cache_cp(yang_type_cache **ycnew, yang_type_cache *ycold)
{
int retval = -1;
int options;
cg_var *mincv;
cg_var *maxcv;
char *pattern;
uint8_t fraction;
yang_stmt *resolved;
yang_type_cache_get(ycold, &resolved, &options, &mincv, &maxcv, &pattern, &fraction);
if (yang_type_cache_set(ycnew, resolved, options, mincv, maxcv, pattern, fraction) < 0)
goto done;
retval = 0;
done:
return retval;
}
int
yang_type_cache_free(yang_type_cache *ycache)
{
if (ycache->yc_mincv)
cv_free(ycache->yc_mincv);
if (ycache->yc_maxcv)
cv_free(ycache->yc_maxcv);
if (ycache->yc_pattern)
free(ycache->yc_pattern);
free(ycache);
return 0;
}
/*! Resolve types: populate type caches */
int
ys_resolve_type(yang_stmt *ys, void *arg)
{
int retval = -1;
int options = 0x0;
cg_var *mincv = NULL;
cg_var *maxcv = NULL;
char *pattern = NULL;
uint8_t fraction = 0;
yang_stmt *resolved = NULL;
if (ys->ys_keyword != Y_TYPE)
return 0;
yang_type_resolve((yang_stmt*)ys->ys_parent, ys, &resolved,
&options, &mincv, &maxcv, &pattern, &fraction);
if (yang_type_cache_set(&ys->ys_typecache,
resolved, options, mincv, maxcv, pattern, fraction) < 0)
goto done;
retval = 0;
done:
return retval;
}
/* return 1 if built-in, 0 if not */
static int
yang_builtin(char *type)
{
const struct map_str2int *yt;
/* built-in types */
for (yt = &ytmap[0]; yt->ms_str; yt++)
if (strcmp(yt->ms_str, type) == 0)
return 1;
return 0;
}
/*! Translate from a yang type to a cligen variable type
*
* Currently many built-in types from RFC6020 and some RFC6991 types.
* But not all, neither built-in nor 6991.
* Also, there is no support for derived types, eg yang typedefs.
* See 4.2.4 in RFC6020
* Return 0 if no match but set cv_type to CGV_ERR
*/
int
yang2cv_type(char *ytype, enum cv_type *cv_type)
{
const struct map_str2int *yt;
*cv_type = CGV_ERR;
/* built-in types */
for (yt = &ytmap[0]; yt->ms_str; yt++)
if (strcmp(yt->ms_str, ytype) == 0){
*cv_type = yt->ms_int;
return 0;
}
/* special derived types */
if (strcmp("ipv4-address", ytype) == 0){ /* RFC6991 */
*cv_type = CGV_IPV4ADDR;
return 0;
}
if (strcmp("ipv6-address", ytype) == 0){ /* RFC6991 */
*cv_type = CGV_IPV6ADDR;
return 0;
}
if (strcmp("ipv4-prefix", ytype) == 0){ /* RFC6991 */
*cv_type = CGV_IPV4PFX;
return 0;
}
if (strcmp("ipv6-prefix", ytype) == 0){ /* RFC6991 */
*cv_type = CGV_IPV6PFX;
return 0;
}
if (strcmp("date-and-time", ytype) == 0){ /* RFC6991 */
*cv_type = CGV_TIME;
return 0;
}
if (strcmp("mac-address", ytype) == 0){ /* RFC6991 */
*cv_type = CGV_MACADDR;
return 0;
}
if (strcmp("uuid", ytype) == 0){ /* RFC6991 */
*cv_type = CGV_UUID;
return 0;
}
return 0;
}
/*! Translate from a cligen variable type to a yang type
*/
char *
cv2yang_type(enum cv_type cv_type)
{
const struct map_str2int *yt;
char *ytype;
ytype = "empty";
/* built-in types */
for (yt = &ytmap[0]; yt->ms_str; yt++)
if (yt->ms_int == cv_type)
return yt->ms_str;
/* special derived types */
if (cv_type == CGV_IPV4ADDR) /* RFC6991 */
return "ipv4-address";
if (cv_type == CGV_IPV6ADDR) /* RFC6991 */
return "ipv6-address";
if (cv_type == CGV_IPV4PFX) /* RFC6991 */
return "ipv4-prefix";
if (cv_type == CGV_IPV6PFX) /* RFC6991 */
return "ipv6-prefix";
if (cv_type == CGV_TIME) /* RFC6991 */
return "date-and-time";
if (cv_type == CGV_MACADDR) /* RFC6991 */
return "mac-address";
if (cv_type == CGV_UUID) /* RFC6991 */
return "uuid";
return ytype;
}
/*! Translate from yang type -> cligen type, after yang resolve has been made.
* handle case where yang resolve did not succedd (rtype=NULL) and then try
* to find special cligen types such as ipv4addr.
* not true yang types
* @param[in] origtype
* @param[in] restype
* @param[out] cvtype
*/
int
clicon_type2cv(char *origtype, char *restype, enum cv_type *cvtype)
{
int retval = -1;
*cvtype = CGV_ERR;
if (restype != NULL){
yang2cv_type(restype, cvtype);
if (*cvtype == CGV_ERR){
clicon_err(OE_DB, 0, "%s: \"%s\" type not translated", __FUNCTION__, restype);
goto done;
}
}
else{
/*
* Not resolved, but we can use special cligen types, eg ipv4addr
* Note this is a kludge or at least if we intend of using rfc types
*/
yang2cv_type(origtype, cvtype);
if (*cvtype == CGV_ERR){
clicon_err(OE_DB, 0, "%s: \"%s\": type not resolved", __FUNCTION__, origtype);
goto done;
}
}
retval = 0;
done:
return retval;
}
/* cf cligen/cligen_var.c */
#define range_check(i, rmin, rmax, type) \
((rmin && (i) < cv_##type##_get(rmin)) || \
(rmax && (i) > cv_##type##_get(rmax)))
/*! Validate cligen variable cv using yang statement as spec
*
* @param [in] cv A cligen variable to validate. This is a correctly parsed cv.
* @param [in] ys A yang statement, must be leaf of leaf-list.
* @param [out] reason If given, and if return value is 0, contains a malloced string
* describing the reason why the validation failed. Must be freed.
* @retval -1 Error (fatal), with errno set to indicate error
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
* @retval 1 Validation OK
* See also cv_validate - the code is similar.
*/
int
ys_cv_validate(cg_var *cv, yang_stmt *ys, char **reason)
{
int retval = 1; /* OK */
cg_var *ycv; /* cv of yang-statement */
int64_t i = 0;
uint64_t u = 0;
char *str;
int options;
cg_var *range_min;
cg_var *range_max;
char *pattern;
int retval2;
enum cv_type cvtype;
char *type; /* orig type */
yang_stmt *yrestype; /* resolved type */
char *restype;
uint8_t fraction;
yang_stmt *yi = NULL;
if (ys->ys_keyword != Y_LEAF && ys->ys_keyword != Y_LEAF_LIST)
return 0;
ycv = ys->ys_cv;
if (yang_type_get(ys, &type, &yrestype,
&options, &range_min, &range_max, &pattern,
&fraction) < 0)
goto err;
restype = yrestype?yrestype->ys_argument:NULL;
if (clicon_type2cv(type, restype, &cvtype) < 0)
goto err;
if (cv_type_get(ycv) != cvtype){
/* special case: dbkey has rest syntax-> cv but yang cant have that */
if (cvtype == CGV_STRING && cv_type_get(ycv) == CGV_REST)
;
else {
clicon_err(OE_DB, 0, "%s: Type mismatch data:%s != yang:%s",
__FUNCTION__, cv_type2str(cvtype), cv_type2str(cv_type_get(ycv)));
goto err;
}
}
switch (cvtype){
case CGV_INT8:
if ((options & YANG_OPTIONS_RANGE) != 0){
i = cv_int8_get(cv);
if (range_check(i, range_min, range_max, int8)){
if (reason)
*reason = cligen_reason("Number out of range: %ld", i);
retval = 0;
break;
}
}
break;
case CGV_INT16:
if ((options & YANG_OPTIONS_RANGE) != 0){
i = cv_int16_get(cv);
if (range_check(i, range_min, range_max, int16)){
if (reason)
*reason = cligen_reason("Number out of range: %ld", i);
retval = 0;
break;
}
}
break;
case CGV_INT32:
if ((options & YANG_OPTIONS_RANGE) != 0){
i = cv_int32_get(cv);
if (range_check(i, range_min, range_max, int32)){
if (reason)
*reason = cligen_reason("Number out of range: %ld", i);
retval = 0;
break;
}
}
break;
case CGV_INT64:
if ((options & YANG_OPTIONS_RANGE) != 0){
i = cv_int64_get(cv);
if (range_check(i, range_min, range_max, int64)){
if (reason)
*reason = cligen_reason("Number out of range: %ld", i);
retval = 0;
break;
}
}
break;
case CGV_UINT8:
if ((options & YANG_OPTIONS_RANGE) != 0){
u = cv_uint8_get(cv);
if (range_check(u, range_min, range_max, uint8)){
if (reason)
*reason = cligen_reason("Number out of range: %lu", u);
retval = 0;
break;
}
}
break;
case CGV_UINT16:
if ((options & YANG_OPTIONS_RANGE) != 0){
u = cv_uint16_get(cv);
if (range_check(u, range_min, range_max, uint16)){
if (reason)
*reason = cligen_reason("Number out of range: %lu", u);
retval = 0;
break;
}
}
break;
case CGV_UINT32:
if ((options & YANG_OPTIONS_RANGE) != 0){
u = cv_uint32_get(cv);
if (range_check(u, range_min, range_max, uint32)){
if (reason)
*reason = cligen_reason("Number out of range: %lu", u);
retval = 0;
break;
}
}
break;
case CGV_UINT64:
if ((options & YANG_OPTIONS_RANGE) != 0){
u = cv_uint64_get(cv);
if (range_check(u, range_min, range_max, uint64)){
if (reason)
*reason = cligen_reason("Number out of range: %lu", u);
retval = 0;
break;
}
}
break;
case CGV_DEC64:
if ((options & YANG_OPTIONS_RANGE) != 0){
i = cv_int64_get(cv);
if (range_check(i, range_min, range_max, int64)){
if (reason)
*reason = cligen_reason("Number out of range: %ld", i);
retval = 0;
break;
}
}
break;
case CGV_STRING:
case CGV_REST:
str = cv_string_get(cv);
if (restype &&
(strcmp(restype, "enumeration") == 0 || strcmp(restype, "bits") == 0)){
int found = 0;
while ((yi = yn_each((yang_node*)yrestype, yi)) != NULL){
if (yi->ys_keyword != Y_ENUM && yi->ys_keyword != Y_BIT)
continue;
if (strcmp(yi->ys_argument, str) == 0){
found++;
break;
}
}
if (!found){
if (reason)
*reason = cligen_reason("'%s' does not match enumeration", str);
retval = 0;
break;
}
}
if ((options & YANG_OPTIONS_LENGTH) != 0){
u = strlen(str);
if (range_check(u, range_min, range_max, uint64)){
if (reason)
*reason = cligen_reason("string length out of range: %lu", u);
retval = 0;
break;
}
}
if ((options & YANG_OPTIONS_PATTERN) != 0){
if ((retval2 = match_regexp(str, pattern)) < 0){
clicon_err(OE_DB, 0, "match_regexp: %s", pattern);
return -1;
}
if (retval2 == 0){
if (reason)
*reason = cligen_reason("regexp match fail: \"%s\" does not match %s",
str, pattern);
retval = 0;
break;
}
}
break;
case CGV_ERR:
case CGV_VOID:
retval = 0;
if (reason)
*reason = cligen_reason("Invalid cv");
retval = 0;
break;
case CGV_BOOL:
case CGV_INTERFACE:
case CGV_IPV4ADDR:
case CGV_IPV6ADDR:
case CGV_IPV4PFX:
case CGV_IPV6PFX:
case CGV_MACADDR:
case CGV_URL:
case CGV_UUID:
case CGV_TIME:
case CGV_EMPTY: /* XXX */
break;
}
if (reason && *reason)
assert(retval == 0);
return retval;
err:
return -1;
}
/*
* a typedef can be under module, submodule, container, list, grouping, rpc,
* input, output, notification
*/
static inline int
ys_typedef(yang_stmt *ys)
{
return ys->ys_keyword == Y_MODULE || ys->ys_keyword == Y_SUBMODULE ||
ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST;
}
/* find next ys up which can contain a typedef */
static yang_stmt *
ys_typedef_up(yang_stmt *ys)
{
yang_node *yn;
while (ys != NULL && !ys_typedef(ys)){
yn = ys->ys_parent;
/* Some extra stuff to ensure ys is a stmt */
if (yn && yn->yn_keyword == Y_SPEC)
yn = NULL;
ys = (yang_stmt*)yn;
}
/* Here it is either NULL or is a typedef-kind yang-stmt */
return (yang_stmt*)ys;
}
/*! Return yang-stmt of identity
This is a sanity check of base identity of identity-ref and for identity
statements.
Return true if node is identityref and is derived from identity_name
The derived-from() function returns true if the (first) node (in
document order in the argument "nodes") is a node of type identityref,
and its value is an identity that is derived from the identity
"identity-name" defined in the YANG module "module-name"; otherwise
it returns false.
Valid values for an identityref are any identities derived from the
identityref's base identity.
1. (base) identity must exist (be found). This is a sanity check
of the specification and also necessary for identity statements.
2. Check if a given node has value derived from base identity. This is
a run-time check necessary when validating eg netconf.
3. Find all valid derived identities from a identityref base identity.
This is for cli generation.
vad är det denna function ska göra? Svar: 1
*/
yang_stmt *
yang_find_identity(yang_stmt *ys, char *identity)
{
char *id;
char *prefix = NULL;
yang_stmt *yimport;
yang_spec *yspec;
yang_stmt *ymodule;
yang_stmt *yid = NULL;
yang_node *yn;
yang_stmt *ymod;
if ((id = strchr(identity, ':')) == NULL)
id = identity;
else{
prefix = strdup(identity);
prefix[id-identity] = '\0';
id++;
}
/* No, now check if identityref is derived from base */
if (prefix){ /* Go to top and find import that matches */
ymod = ys_module(ys);
if ((yimport = ys_module_import(ymod, prefix)) == NULL)
goto done;
yspec = ys_spec(ys);
if ((ymodule = yang_find((yang_node*)yspec, Y_MODULE, yimport->ys_argument)) == NULL)
goto done; /* unresolved */
yid = yang_find((yang_node*)ymodule, Y_IDENTITY, id);
}
else{
while (1){
/* Check upwards in hierarchy for matching typedefs */
if ((ys = ys_typedef_up(ys)) == NULL) /* If reach top */
break;
/* Here find identity */
if ((yid = yang_find((yang_node*)ys, Y_IDENTITY, id)) != NULL)
break;
/* Did not find a matching typedef there, proceed to next level */
yn = ys->ys_parent;
if (yn && yn->yn_keyword == Y_SPEC)
yn = NULL;
ys = (yang_stmt*)yn;
}
}
done:
if (prefix)
free(prefix);
return yid;
}
/*
*/
static int
resolve_restrictions(yang_stmt *yrange,
yang_stmt *ylength,
yang_stmt *ypattern,
yang_stmt *yfraction,
int *options,
cg_var **mincv,
cg_var **maxcv,
char **pattern,
uint8_t *fraction)
{
if (options && mincv && maxcv && yrange != NULL){
*mincv = cvec_find(yrange->ys_cvec, "range_min");
*maxcv = cvec_find(yrange->ys_cvec, "range_max");
*options |= YANG_OPTIONS_RANGE;
}
if (options && mincv && maxcv && ylength != NULL){
*mincv = cvec_find(ylength->ys_cvec, "range_min"); /* XXX fel typ */
*maxcv = cvec_find(ylength->ys_cvec, "range_max");
*options |= YANG_OPTIONS_LENGTH;
}
if (options && pattern && ypattern != NULL){
*pattern = ypattern->ys_argument;
*options |= YANG_OPTIONS_PATTERN;
}
if (options && fraction && yfraction != NULL){
*fraction = cv_uint8_get(yfraction->ys_cv);
*options |= YANG_OPTIONS_FRACTION_DIGITS;
}
return 0;
}
/*! Recursively resolve a yang type to built-in type with optional restrictions
* @param [in] ys yang-stmt from where the current search is based
* @param [in] ytype yang-stmt object containing currently resolving type
* @param [out] yrestype resolved type. return built-in type or NULL. mandatory
* @param [out] options pointer to flags field of optional values. optional
* @param [out] mincv pointer to cv with min range or length. If options&YANG_OPTIONS_RANGE
* @param [out] maxcv pointer to cv with max range or length. If options&YANG_OPTIONS_RANGE
* @param [out] pattern pointer to static string of yang string pattern. optional
* @param [out] fraction for decimal64, how many digits after period
* @retval 0 OK. Note yrestype may still be NULL.
* @retval -1 Error, clicon_err handles errors
* The setting of the options argument has the following semantics:
* options&YANG_OPTIONS_RANGE or YANG_OPTIONS_LENGTH --> mincv and max _can_ be set
* options&YANG_OPTIONS_PATTERN --> pattern is set
* options&YANG_OPTIONS_FRACTION_DIGITS --> fraction is set
* Note that the static output strings (type, pattern) should be copied if used asap.
* Note also that for all pointer arguments, if NULL is given, no value is assigned.
*/
int
yang_type_resolve(yang_stmt *ys,
yang_stmt *ytype,
yang_stmt **yrestype,
int *options,
cg_var **mincv,
cg_var **maxcv,
char **pattern,
uint8_t *fraction)
{
yang_stmt *rytypedef = NULL; /* Resolved typedef of ytype */
yang_stmt *rytype; /* Resolved type of ytype */
yang_stmt *yrange;
yang_stmt *ylength;
yang_stmt *ypattern;
yang_stmt *yfraction;
yang_stmt *yimport;
char *type;
char *prefix = NULL;
int retval = -1;
yang_node *yn;
yang_spec *yspec;
yang_stmt *ymod;
if (options)
*options = 0x0;
*yrestype = NULL; /* Initialization of resolved type that may not be necessary */
type = ytype_id(ytype); /* This is the type to resolve */
prefix = ytype_prefix(ytype); /* And this its prefix */
if (ytype->ys_typecache != NULL){
if (yang_type_cache_get(ytype->ys_typecache,
yrestype, options, mincv, maxcv, pattern, fraction) < 0)
goto done;
goto ok;
}
yrange = yang_find((yang_node*)ytype, Y_RANGE, NULL);
ylength = yang_find((yang_node*)ytype, Y_LENGTH, NULL);
ypattern = yang_find((yang_node*)ytype, Y_PATTERN, NULL);
yfraction = yang_find((yang_node*)ytype, Y_FRACTION_DIGITS, NULL);
/* Check if type is basic type. If so, return that */
if (prefix == NULL && yang_builtin(type)){
*yrestype = ytype;
resolve_restrictions(yrange, ylength, ypattern, yfraction, options,
mincv, maxcv, pattern, fraction);
goto ok;
}
/* Not basic type. Now check if prefix which means we look in other module */
if (prefix){ /* Go to top and find import that matches */
ymod = ys_module(ys);
if ((yimport = ys_module_import(ymod, prefix)) == NULL){
clicon_err(OE_DB, 0, "Prefix %s not defined not found", prefix);
goto done;
}
yspec = ys_spec(ys);
if ((ymod = yang_find((yang_node*)yspec, Y_MODULE, yimport->ys_argument)) == NULL)
goto ok; /* unresolved */
if ((rytypedef = yang_find((yang_node*)ymod, Y_TYPEDEF, type)) == NULL)
goto ok; /* unresolved */
}
else
while (1){
/* Check upwards in hierarchy for matcing typedefs */
if ((ys = ys_typedef_up(ys)) == NULL){ /* If reach top */
*yrestype = NULL;
break;
}
/* Here find typedef */
if ((rytypedef = yang_find((yang_node*)ys, Y_TYPEDEF, type)) != NULL)
break;
/* Did not find a matching typedef there, proceed to next level */
yn = ys->ys_parent;
if (yn && yn->yn_keyword == Y_SPEC)
yn = NULL;
ys = (yang_stmt*)yn;
}
if (rytypedef != NULL){ /* We have found a typedef */
/* Find associated type statement */
if ((rytype = yang_find((yang_node*)rytypedef, Y_TYPE, NULL)) == NULL){
clicon_err(OE_DB, 0, "%s: mandatory type object is not found", __FUNCTION__);
goto done;
}
/* recursively resolve this new type */
if (yang_type_resolve(ys, rytype, yrestype,
options, mincv, maxcv, pattern, fraction) < 0)
goto done;
/* overwrites the resolved if any */
resolve_restrictions(yrange, ylength, ypattern, yfraction,
options, mincv, maxcv, pattern, fraction);
}
ok:
retval = 0;
done:
if (prefix)
free(prefix);
return retval;
}
/*! Get type information about a leaf/leaf-list yang-statement
*
* @code
* yang_stmt *yrestype;
* int options;
* int64_t min, max;
* char *pattern;
* uint8_t fraction;
*
* if (yang_type_get(ys, &type, &yrestype, &options, &min, &max, &pattern, &fraction) < 0)
* goto err;
* if (yrestype == NULL) # unresolved
* goto err;
* if (options & YANG_OPTIONS_LENGTH != 0)
* printf("%d..%d\n", min , max);
* if (options & YANG_OPTIONS_PATTERN != 0)
* printf("regexp: %s\n", pattern);
* @endcode
* @param [in] ys yang-stmt, leaf or leaf-list
* @param [out] origtype original type may be derived or built-in
* @param [out] yrestype pointer to resolved type stmt. should be built-in or NULL
* @param [out] options pointer to flags field of optional values
* @param [out] mincv pointer to cv of min range or length. optional
* @param [out] maxcv pointer to cv of max range or length. optional
* @param [out] pattern pointer to static string of yang string pattern. optional
* @param [out] fraction for decimal64, how many digits after period
* @retval 0 OK, but note that restype==NULL means not resolved.
* @retval -1 Error, clicon_err handles errors
* The setting of the options argument has the following semantics:
* options&YANG_OPTIONS_RANGE or YANG_OPTIONS_LENGTH --> mincv and max _can_ be set
* options&YANG_OPTIONS_PATTERN --> pattern is set
* options&YANG_OPTIONS_FRACTION_DIGITS --> fraction is set
* Note that the static output strings (type, pattern) should be copied if used asap.
* Note also that for all pointer arguments, if NULL is given, no value is assigned.
* @See yang_type_resolve(). This function is really just a frontend to that.
*/
int
yang_type_get(yang_stmt *ys,
char **origtype,
yang_stmt **yrestype,
int *options,
cg_var **mincv,
cg_var **maxcv,
char **pattern,
uint8_t *fraction
)
{
int retval = -1;
yang_stmt *ytype; /* type */
char *type;
if (options)
*options = 0x0;
/* Find mandatory type */
if ((ytype = yang_find((yang_node*)ys, Y_TYPE, NULL)) == NULL){
clicon_err(OE_DB, 0, "%s: mandatory type object is not found", __FUNCTION__);
goto done;
}
type = ytype_id(ytype);
if (origtype)
*origtype = type;
if (yang_type_resolve(ys, ytype, yrestype,
options, mincv, maxcv, pattern, fraction) < 0)
goto done;
clicon_debug(3, "%s: %s %s->%s", __FUNCTION__, ys->ys_argument, type,
*yrestype?(*yrestype)->ys_argument:"null");
retval = 0;
done:
return retval;
}