Inital commit
This commit is contained in:
parent
edc5e091bb
commit
d6e393ea58
145 changed files with 58117 additions and 0 deletions
73
lib/Makefile.in
Normal file
73
lib/Makefile.in
Normal 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
44
lib/clicon/Makefile.in
Normal 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
78
lib/clicon/clicon.h.in
Normal 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
166
lib/clicon/clicon_chunk.h
Normal 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
80
lib/clicon/clicon_err.h
Normal 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
46
lib/clicon/clicon_event.h
Normal 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
36
lib/clicon/clicon_file.h
Normal 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_ */
|
||||
61
lib/clicon/clicon_handle.h
Normal file
61
lib/clicon/clicon_handle.h
Normal 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
70
lib/clicon/clicon_hash.h
Normal 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
57
lib/clicon/clicon_log.h
Normal 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
114
lib/clicon/clicon_options.h
Normal 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_ */
|
||||
67
lib/clicon/clicon_plugin.h
Normal file
67
lib/clicon/clicon_plugin.h
Normal 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
33
lib/clicon/clicon_proc.h
Normal 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
183
lib/clicon/clicon_proto.h
Normal 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_ */
|
||||
57
lib/clicon/clicon_proto_client.h
Normal file
57
lib/clicon/clicon_proto_client.h
Normal 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_ */
|
||||
189
lib/clicon/clicon_proto_encode.h
Normal file
189
lib/clicon/clicon_proto_encode.h
Normal 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
58
lib/clicon/clicon_qdb.h
Normal 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
89
lib/clicon/clicon_queue.h
Normal 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
30
lib/clicon/clicon_sha1.h
Normal 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
42
lib/clicon/clicon_sig.h
Normal 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_ */
|
||||
56
lib/clicon/clicon_string.h
Normal file
56
lib/clicon/clicon_string.h
Normal 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
108
lib/clicon/clicon_xml.h
Normal 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 */
|
||||
42
lib/clicon/clicon_xml_db.h
Normal file
42
lib/clicon/clicon_xml_db.h
Normal 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 */
|
||||
53
lib/clicon/clicon_xml_map.h
Normal file
53
lib/clicon/clicon_xml_map.h
Normal 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
33
lib/clicon/clicon_xsl.h
Normal 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
206
lib/clicon/clicon_yang.h
Normal 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_ */
|
||||
68
lib/clicon/clicon_yang_type.h
Normal file
68
lib/clicon/clicon_yang_type.h
Normal 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
173
lib/src/Makefile.in
Normal 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
778
lib/src/clicon_chunk.c
Normal 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
227
lib/src/clicon_err.c
Normal 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
309
lib/src/clicon_event.c
Normal 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 = ⅇ
|
||||
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
348
lib/src/clicon_file.c
Normal 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
155
lib/src/clicon_handle.c
Normal 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
343
lib/src/clicon_hash.c
Normal 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
332
lib/src/clicon_log.c
Normal 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
684
lib/src/clicon_options.c
Normal 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
73
lib/src/clicon_plugin.c
Normal 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
354
lib/src/clicon_proc.c
Normal 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), >mp) < 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
515
lib/src/clicon_proto.c
Normal 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;
|
||||
}
|
||||
|
||||
524
lib/src/clicon_proto_client.c
Normal file
524
lib/src/clicon_proto_client.c
Normal 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;
|
||||
}
|
||||
|
||||
1038
lib/src/clicon_proto_encode.c
Normal file
1038
lib/src/clicon_proto_encode.c
Normal file
File diff suppressed because it is too large
Load diff
462
lib/src/clicon_qdb.c
Normal file
462
lib/src/clicon_qdb.c
Normal 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
448
lib/src/clicon_sha1.c
Normal 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
185
lib/src/clicon_sig.c
Normal 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
346
lib/src/clicon_string.c
Normal 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
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
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
845
lib/src/clicon_xml_map.c
Normal 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;
|
||||
}
|
||||
50
lib/src/clicon_xml_parse.h
Normal file
50
lib/src/clicon_xml_parse.h
Normal 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
138
lib/src/clicon_xml_parse.l
Normal 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
344
lib/src/clicon_xml_parse.y
Normal 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
BIN
lib/src/clicon_xpath
Normal file
Binary file not shown.
831
lib/src/clicon_xsl.c
Normal file
831
lib/src/clicon_xsl.c
Normal 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 då?
|
||||
* 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
2034
lib/src/clicon_yang.c
Normal file
File diff suppressed because it is too large
Load diff
85
lib/src/clicon_yang_parse.h
Normal file
85
lib/src/clicon_yang_parse.h
Normal 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
260
lib/src/clicon_yang_parse.l
Normal 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
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
910
lib/src/clicon_yang_type.c
Normal 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.
|
||||
Så 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue