From 3120ac3f78ded9483b9ca8a2bc1e113eff78baf7 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 30 Apr 2010 14:19:48 +0200 Subject: remove the unneeded bts_link pointer from msgb --- include/osmocore/msgb.h | 5 ----- src/msgb.c | 1 - 2 files changed, 6 deletions(-) diff --git a/include/osmocore/msgb.h b/include/osmocore/msgb.h index 31db7194..31e54dcd 100644 --- a/include/osmocore/msgb.h +++ b/include/osmocore/msgb.h @@ -23,14 +23,9 @@ #include #include "linuxlist.h" -struct bts_link; - struct msgb { struct llist_head list; - /* ptr to the physical E1 link to the BTS(s) */ - struct gsm_bts_link *bts_link; - /* Part of which TRX logical channel we were received / transmitted */ struct gsm_bts_trx *trx; struct gsm_lchan *lchan; diff --git a/src/msgb.c b/src/msgb.c index 60af373e..aea92d44 100644 --- a/src/msgb.c +++ b/src/msgb.c @@ -80,7 +80,6 @@ void msgb_reset(struct msgb *msg) msg->head = msg->_data; msg->tail = msg->_data; - msg->bts_link = NULL; msg->trx = NULL; msg->lchan = NULL; msg->l2h = NULL; -- cgit v1.2.3 From bb77c9d6cc6c1353817629f45b1414c9b0668b39 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 30 Apr 2010 14:26:12 +0200 Subject: msgb: remove smsh, llch, tlli and gmmh This is a lot of GSM/GPRS specific stuff in struct msgb which we want to avoid. The 'control buffer' will replace them. --- include/osmocore/msgb.h | 13 ++----------- src/msgb.c | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/include/osmocore/msgb.h b/include/osmocore/msgb.h index 31e54dcd..61c224fe 100644 --- a/include/osmocore/msgb.h +++ b/include/osmocore/msgb.h @@ -36,17 +36,8 @@ struct msgb { unsigned char *l2h; /* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */ unsigned char *l3h; - /* the layer 4 header */ - union { - unsigned char *smsh; - unsigned char *llch; - unsigned char *l4h; - }; - - /* the layer 5 header, GPRS: GMM header */ - unsigned char *gmmh; - uint32_t tlli; + unsigned char *l4h; uint16_t data_len; uint16_t len; @@ -66,7 +57,7 @@ extern void msgb_reset(struct msgb *m); #define msgb_l1(m) ((void *)(m->l1h)) #define msgb_l2(m) ((void *)(m->l2h)) #define msgb_l3(m) ((void *)(m->l3h)) -#define msgb_sms(m) ((void *)(m->smsh)) +#define msgb_sms(m) ((void *)(m->l4h)) static inline unsigned int msgb_l1len(const struct msgb *msgb) { diff --git a/src/msgb.c b/src/msgb.c index aea92d44..9117a0ad 100644 --- a/src/msgb.c +++ b/src/msgb.c @@ -84,5 +84,5 @@ void msgb_reset(struct msgb *msg) msg->lchan = NULL; msg->l2h = NULL; msg->l3h = NULL; - msg->smsh = NULL; + msg->l4h = NULL; } -- cgit v1.2.3 From 074c9f904cb5e4f6ab014d76e4abc079c16fc5d7 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 30 Apr 2010 14:29:11 +0200 Subject: msgb: introduce msgb->cb (the control buffer) --- include/osmocore/msgb.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/osmocore/msgb.h b/include/osmocore/msgb.h index 61c224fe..2841dc56 100644 --- a/include/osmocore/msgb.h +++ b/include/osmocore/msgb.h @@ -27,6 +27,7 @@ struct msgb { struct llist_head list; /* Part of which TRX logical channel we were received / transmitted */ + /* FIXME: move them into the control buffer */ struct gsm_bts_trx *trx; struct gsm_lchan *lchan; @@ -39,6 +40,9 @@ struct msgb { /* the layer 4 header */ unsigned char *l4h; + /* the 'control buffer', large enough to contain 5 pointers */ + unsigned long cb[5]; + uint16_t data_len; uint16_t len; -- cgit v1.2.3 From 5ba4dc171b0d86ae3032c3732334481d9d399188 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Sat, 1 May 2010 15:09:48 +0800 Subject: Add missing file. --- include/osmocore/protocol/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/osmocore/protocol/Makefile.am b/include/osmocore/protocol/Makefile.am index 6d8883e6..557950ec 100644 --- a/include/osmocore/protocol/Makefile.am +++ b/include/osmocore/protocol/Makefile.am @@ -1,3 +1,3 @@ -osmocore_proto_HEADERS = gsm_04_08.h gsm_04_11.h gsm_04_80.h gsm_08_58.h gsm_12_21.h +osmocore_proto_HEADERS = gsm_04_08.h gsm_04_11.h gsm_04_80.h gsm_08_58.h gsm_12_21.h gsm_08_08.h osmocore_protodir = $(includedir)/osmocore/protocol -- cgit v1.2.3 From a1c4f765eca695bf3d9c41d741bcab81989c8d48 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 1 May 2010 11:59:42 +0200 Subject: import gsm48_parse_ra() and gprs_tlli_type() from openbsc --- include/osmocore/gsm48.h | 11 +++++++++++ include/osmocore/gsm_utils.h | 12 ++++++++++++ include/osmocore/protocol/gsm_04_08.h | 9 ++++++++- src/gsm48.c | 21 +++++++++++++++++++++ src/gsm_utils.c | 15 +++++++++++++++ 5 files changed, 67 insertions(+), 1 deletion(-) diff --git a/include/osmocore/gsm48.h b/include/osmocore/gsm48.h index e3a1defa..9015c25b 100644 --- a/include/osmocore/gsm48.h +++ b/include/osmocore/gsm48.h @@ -4,6 +4,14 @@ #include #include +/* A parsed GPRS routing area */ +struct gprs_ra_id { + uint16_t mnc; + uint16_t mcc; + uint16_t lac; + uint8_t rac; +}; + extern const struct tlv_definition gsm48_att_tlvdef; const char *gsm48_cc_state_name(uint8_t state); const char *gsm48_cc_msg_name(uint8_t msgtype); @@ -18,4 +26,7 @@ int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi); int gsm48_mi_to_string(char *string, const int str_len, const uint8_t *mi, const int mi_len); +/* Parse Routeing Area Identifier */ +void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf); + #endif diff --git a/include/osmocore/gsm_utils.h b/include/osmocore/gsm_utils.h index 195e865c..51e9f2e6 100644 --- a/include/osmocore/gsm_utils.h +++ b/include/osmocore/gsm_utils.h @@ -87,5 +87,17 @@ void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn); /* Convert from GSM time to frame number */ uint32_t gsm_gsmtime2fn(struct gsm_time *time); +/* GSM TS 03.03 Chapter 2.6 */ +enum gprs_tlli_tyoe { + TLLI_LOCAL, + TLLI_FOREIGN, + TLLI_RANDOM, + TLLI_AUXILIARY, + TLLI_RESERVED, +}; + +/* TS 03.03 Chapter 2.6 */ +int gprs_tlli_type(uint32_t tlli); + void generate_backtrace(); #endif diff --git a/include/osmocore/protocol/gsm_04_08.h b/include/osmocore/protocol/gsm_04_08.h index 47b98b29..1a112a08 100644 --- a/include/osmocore/protocol/gsm_04_08.h +++ b/include/osmocore/protocol/gsm_04_08.h @@ -735,10 +735,17 @@ enum gsm48_bcap_rrq { GSM48_BCAP_RRQ_DUAL_FR = 3, }; - #define GSM48_TMSI_LEN 5 #define GSM48_MID_TMSI_LEN (GSM48_TMSI_LEN + 2) #define GSM48_MI_SIZE 32 +/* Chapter 10.4.4.15 */ +struct gsm48_ra_id { + uint8_t digits[3]; /* MCC + MNC BCD digits */ + uint16_t lac; /* Location Area Code */ + uint8_t rac; /* Routing Area Code */ +} __attribute__ ((packed)); + + #endif /* PROTO_GSM_04_08_H */ diff --git a/src/gsm48.c b/src/gsm48.c index 783ff6a5..7e510664 100644 --- a/src/gsm48.c +++ b/src/gsm48.c @@ -305,3 +305,24 @@ int gsm48_mi_to_string(char *string, const int str_len, const uint8_t *mi, return str_cur - string; } + +void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf) +{ + raid->mcc = (buf[0] & 0xf) * 100; + raid->mcc += (buf[0] >> 4) * 10; + raid->mcc += (buf[1] & 0xf) * 1; + + /* I wonder who came up with the stupidity of encoding the MNC + * differently depending on how many digits its decimal number has! */ + if ((buf[1] >> 4) == 0xf) { + raid->mnc = (buf[2] & 0xf) * 10; + raid->mnc += (buf[2] >> 4) * 1; + } else { + raid->mnc = (buf[2] & 0xf) * 100; + raid->mnc += (buf[2] >> 4) * 10; + raid->mnc += (buf[1] >> 4) * 1; + } + + raid->lac = ntohs(*(uint16_t *)(buf + 3)); + raid->rac = buf[5]; +} diff --git a/src/gsm_utils.c b/src/gsm_utils.c index 593dd5c9..b392fd37 100644 --- a/src/gsm_utils.c +++ b/src/gsm_utils.c @@ -359,3 +359,18 @@ uint32_t gsm_gsmtime2fn(struct gsm_time *time) /* TS 05.02 Chapter 4.3.3 TDMA frame number */ return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1)); } + +/* TS 03.03 Chapter 2.6 */ +int gprs_tlli_type(uint32_t tlli) +{ + if ((tlli & 0xc0000000) == 0xc0000000) + return TLLI_LOCAL; + else if ((tlli & 0xc0000000) == 0x80000000) + return TLLI_FOREIGN; + else if ((tlli & 0xf8000000) == 0x78000000) + return TLLI_RANDOM; + else if ((tlli & 0xf8000000) == 0x70000000) + return TLLI_AUXILIARY; + + return TLLI_RESERVED; +} -- cgit v1.2.3 From debf95507461965aa82be2fa2bf34119343cfb0e Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 1 May 2010 12:06:48 +0200 Subject: gsm48.h: Prevent accidental re-inclusion of same header file --- include/osmocore/gsm48.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/osmocore/gsm48.h b/include/osmocore/gsm48.h index 9015c25b..a509a096 100644 --- a/include/osmocore/gsm48.h +++ b/include/osmocore/gsm48.h @@ -1,4 +1,5 @@ #ifndef _OSMOCORE_GSM48_H +#define _OSMOCORE_GSM48_H #include #include -- cgit v1.2.3 From 35a939463eee58492c88cbe7531288237cdcf454 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 1 May 2010 14:25:22 +0200 Subject: Import gsm48_construct_ra() from openbsc --- include/osmocore/gsm48.h | 1 + src/gsm48.c | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/include/osmocore/gsm48.h b/include/osmocore/gsm48.h index a509a096..be9fbbd1 100644 --- a/include/osmocore/gsm48.h +++ b/include/osmocore/gsm48.h @@ -29,5 +29,6 @@ int gsm48_mi_to_string(char *string, const int str_len, /* Parse Routeing Area Identifier */ void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf); +int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid); #endif diff --git a/src/gsm48.c b/src/gsm48.c index 7e510664..d957aef6 100644 --- a/src/gsm48.c +++ b/src/gsm48.c @@ -326,3 +326,28 @@ void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf) raid->lac = ntohs(*(uint16_t *)(buf + 3)); raid->rac = buf[5]; } + +int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid) +{ + uint16_t mcc = raid->mcc; + uint16_t mnc = raid->mnc; + + buf[0] = ((mcc / 100) % 10) | (((mcc / 10) % 10) << 4); + buf[1] = (mcc % 10); + + /* I wonder who came up with the stupidity of encoding the MNC + * differently depending on how many digits its decimal number has! */ + if (mnc < 100) { + buf[1] |= 0xf0; + buf[2] = ((mnc / 10) % 10) | ((mnc % 10) << 4); + } else { + buf[1] |= (mnc % 10) << 4; + buf[2] = ((mnc / 100) % 10) | (((mcc / 10) % 10) << 4); + } + + *(uint16_t *)(buf+3) = htons(raid->lac); + + buf[5] = raid->rac; + + return 6; +} -- cgit v1.2.3 From 95df5c0179f6b24d31c6720a43434755ed58aa22 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 1 May 2010 23:53:26 +0200 Subject: msgb: initialize cb[] to zero during msgb_reset() --- src/msgb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/msgb.c b/src/msgb.c index 9117a0ad..a60e2ffa 100644 --- a/src/msgb.c +++ b/src/msgb.c @@ -85,4 +85,6 @@ void msgb_reset(struct msgb *msg) msg->l2h = NULL; msg->l3h = NULL; msg->l4h = NULL; + + memset(&msg->cb, 0, sizeof(msg->cb)); } -- cgit v1.2.3 From 9ac2225ff43e7ec74ec6a629383899f3b6e78d2c Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 11 May 2010 11:19:40 +0200 Subject: logging: introuduce log_level_str() to obtain the name of a log level --- include/osmocore/logging.h | 1 + src/logging.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/osmocore/logging.h b/include/osmocore/logging.h index 93f18a07..b9e0c8fd 100644 --- a/include/osmocore/logging.h +++ b/include/osmocore/logging.h @@ -117,6 +117,7 @@ void log_set_print_timestamp(struct log_target *target, int); void log_set_log_level(struct log_target *target, int log_level); void log_parse_category_mask(struct log_target *target, const char* mask); int log_parse_level(const char *lvl); +const char *log_level_str(unsigned int lvl); int log_parse_category(const char *category); void log_set_category_filter(struct log_target *target, int category, int enable, int level); diff --git a/src/logging.c b/src/logging.c index 7c508771..e5d459a0 100644 --- a/src/logging.c +++ b/src/logging.c @@ -58,6 +58,11 @@ int log_parse_level(const char *lvl) return get_string_value(loglevel_strs, lvl); } +const char *log_level_str(unsigned int lvl) +{ + return get_value_string(loglevel_strs, lvl); +} + int log_parse_category(const char *category) { int i; -- cgit v1.2.3 From 7638af95fd08213aef4adb3c6399975fe3621855 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 11 May 2010 16:39:22 +0200 Subject: logging: add log_vty_{level,category}_string() --- include/osmocore/logging.h | 4 ++++ src/logging.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/include/osmocore/logging.h b/include/osmocore/logging.h index b9e0c8fd..2e82959a 100644 --- a/include/osmocore/logging.h +++ b/include/osmocore/logging.h @@ -128,4 +128,8 @@ struct log_target *log_target_create_stderr(void); void log_add_target(struct log_target *target); void log_del_target(struct log_target *target); +/* Gernerate command argument strings for VTY use */ +const char *log_vty_category_string(struct log_info *info); +const char *log_vty_level_string(struct log_info *info); + #endif /* _OSMOCORE_LOGGING_H */ diff --git a/src/logging.c b/src/logging.c index e5d459a0..e72a6e20 100644 --- a/src/logging.c +++ b/src/logging.c @@ -366,6 +366,52 @@ struct log_target *log_target_create_stderr(void) #endif /* stderr */ } +const char *log_vty_level_string(struct log_info *info) +{ + const struct value_string *vs; + unsigned int len = 3; /* ()\0 */ + char *str; + + for (vs = loglevel_strs; vs->value || vs->str; vs++) + len += strlen(vs->str) + 1; + + str = talloc_zero_size(NULL, len); + if (!str) + return NULL; + + str[0] = '('; + for (vs = loglevel_strs; vs->value || vs->str; vs++) { + strcat(str, vs->str); + strcat(str, "|"); + } + str[strlen(str)-1] = ')'; + + return str; +} + +const char *log_vty_category_string(struct log_info *info) +{ + unsigned int len = 3; /* "()\0" */ + unsigned int i; + char *str; + + for (i = 0; i < info->num_cat; i++) + len += strlen(info->cat[i].name) + 1; + + str = talloc_zero_size(NULL, len); + if (!str) + return NULL; + + str[0] = '('; + for (i = 0; i < info->num_cat; i++) { + strcat(str, info->cat[i].name+1); + strcat(str, "|"); + } + str[strlen(str)-1] = ')'; + + return str; +} + void log_init(const struct log_info *cat) { tall_log_ctx = talloc_named_const(NULL, 1, "logging"); -- cgit v1.2.3 From 7b45d608872f17ab8b71d53a2d87e5f8d621b007 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 13 May 2010 11:35:30 +0200 Subject: Add new 'rate counter' implementation to libosmocore A 'rate counter' is a counter that counts events but also keeps track of the rate of events (per second, minute, hour and day). 'rate counters' are generally abstracted in 'rate counter groups', which are instances of a 'rate counter group description'. This way we can have e.g. a description describing what kind of counters a BTS (or TRX) has - and we can then create one instance of that group for every BTS or TRX that exists. --- include/osmocore/Makefile.am | 2 +- include/osmocore/rate_ctr.h | 81 ++++++++++++++++++++++++++++ src/Makefile.am | 2 +- src/rate_ctr.c | 124 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 include/osmocore/rate_ctr.h create mode 100644 src/rate_ctr.c diff --git a/include/osmocore/Makefile.am b/include/osmocore/Makefile.am index 56f926bc..fde01027 100644 --- a/include/osmocore/Makefile.am +++ b/include/osmocore/Makefile.am @@ -1,7 +1,7 @@ osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h \ tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \ gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h mncc.h \ - gsm48_ie.h logging.h gsm0808.h + gsm48_ie.h logging.h gsm0808.h rate_ctr.h if ENABLE_TALLOC osmocore_HEADERS += talloc.h diff --git a/include/osmocore/rate_ctr.h b/include/osmocore/rate_ctr.h new file mode 100644 index 00000000..c6a1ace2 --- /dev/null +++ b/include/osmocore/rate_ctr.h @@ -0,0 +1,81 @@ +#ifndef _RATE_CTR_H +#define _RATE_CTR_H + +#include + +#include + +#define RATE_CTR_INTV_NUM 4 + +enum rate_ctr_intv { + RATE_CTR_INTV_SEC, + RATE_CTR_INTV_MIN, + RATE_CTR_INTV_HOUR, + RATE_CTR_INTV_DAY, +}; + +/* for each of the intervals, we keep the following values */ +struct rate_ctr_per_intv { + uint64_t last; + uint64_t rate; +}; + +/* for each actual value, we keep the following data */ +struct rate_ctr { + uint64_t current; + struct rate_ctr_per_intv intv[RATE_CTR_INTV_NUM]; +}; + +struct rate_ctr_desc { + const char *name; + const char *description; +}; + +/* Describe a counter group class */ +struct rate_ctr_group_desc { + /* The prefix / format string to be used for all counters */ + char *group_prefix_fmt; + /* The human-readable description of the group */ + char *group_description; + /* The number of counters in this group */ + unsigned int num_ctr; + /* Pointer to array of counter names */ + struct rate_ctr_desc *ctr_desc; +}; + +/* One instance of a counter group class */ +struct rate_ctr_group { + /* Linked list of all counter groups in the system */ + struct llist_head list; + /* Pointer to the counter group class */ + const struct rate_ctr_group_desc *desc; + /* The name prefix generated from desc->group_prefix_fmt and index */ + char *name_prefix; + /* Actual counter structures below */ + struct rate_ctr ctr[0]; +}; + +/* Allocate a new group of counters according to description */ +struct rate_ctr_group *rate_ctr_group_alloc(void *ctx, + const struct rate_ctr_group_desc *desc, + unsigned int idx); + +/* Free the memory for the specified group of counters */ +void rate_ctr_group_free(struct rate_ctr_group *grp); + +/* Add a number to the counter */ +void rate_ctr_add(struct rate_ctr *ctr, int inc); + +/* Increment the counter by 1 */ +static inline void rate_ctr_inc(struct rate_ctr *ctr) +{ + rate_ctr_add(ctr, 1); +} + +/* Initialize the counter module */ +int rate_ctr_init(void *tall_ctx); + +struct vty; +void vty_out_rate_ctr_group(struct vty *vty, const char *prefix, + struct rate_ctr_group *ctrg); +#endif /* RATE_CTR_H */ diff --git a/src/Makefile.am b/src/Makefile.am index 20e99db3..ce063d05 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,7 +10,7 @@ lib_LTLIBRARIES = libosmocore.la libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c rxlev_stat.c \ tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \ write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c \ - logging.c gsm0808.c + logging.c gsm0808.c rate_ctr.c if ENABLE_TALLOC libosmocore_la_SOURCES += talloc.c diff --git a/src/rate_ctr.c b/src/rate_ctr.c new file mode 100644 index 00000000..9b2459e8 --- /dev/null +++ b/src/rate_ctr.c @@ -0,0 +1,124 @@ +/* utility routines for keeping conters about events and the event rates */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include + +#include +#include +#include +#include + +static LLIST_HEAD(rate_ctr_groups); + +static void *tall_rate_ctr_ctx; + +struct rate_ctr_group *rate_ctr_group_alloc(void *ctx, + const struct rate_ctr_group_desc *desc, + unsigned int idx) +{ + unsigned int size; + struct rate_ctr_group *group; + + size = sizeof(struct rate_ctr_group) + + desc->num_ctr * sizeof(struct rate_ctr); + + if (!ctx) + ctx = tall_rate_ctr_ctx; + + group = talloc_zero_size(ctx, size); + if (!group) + return NULL; + + group->desc = desc; + /* Generate the Group prefix from the user-specified index */ + group->name_prefix = talloc_size(group, strlen(desc->group_prefix_fmt) + 20); + sprintf(group->name_prefix, desc->group_prefix_fmt, idx); + + llist_add(&group->list, &rate_ctr_groups); + + return group; +} + +void rate_ctr_group_free(struct rate_ctr_group *grp) +{ + llist_del(&grp->list); + talloc_free(grp); +} + +void rate_ctr_add(struct rate_ctr *ctr, int inc) +{ + ctr->current += inc; +} + +static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv) +{ + /* calculate rate over last interval */ + ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last; + /* save current counter for next interval */ + ctr->intv[intv].last = ctr->current; +} + +static struct timer_list rate_ctr_timer; +static uint64_t timer_ticks; + +/* The one-second interval has expired */ +static void rate_ctr_group_intv(struct rate_ctr_group *grp) +{ + unsigned int i; + + for (i = 0; i < grp->desc->num_ctr; i++) { + struct rate_ctr *ctr = &grp->ctr[i]; + + interval_expired(ctr, RATE_CTR_INTV_SEC); + if ((timer_ticks % 60) == 0) + interval_expired(ctr, RATE_CTR_INTV_MIN); + if ((timer_ticks % (60*60)) == 0) + interval_expired(ctr, RATE_CTR_INTV_HOUR); + if ((timer_ticks % (24*60*60)) == 0) + interval_expired(ctr, RATE_CTR_INTV_DAY); + } +} + +static void rate_ctr_timer_cb(void *data) +{ + struct rate_ctr_group *ctrg; + + /* Increment number of ticks before we calculate intervals, + * as a counter value of 0 would already wrap all counters */ + timer_ticks++; + + llist_for_each_entry(ctrg, &rate_ctr_groups, list) + rate_ctr_group_intv(ctrg); + + bsc_schedule_timer(&rate_ctr_timer, 1, 0); +} + +int rate_ctr_init(void *tall_ctx) +{ + tall_rate_ctr_ctx = tall_ctx; + rate_ctr_timer.cb = rate_ctr_timer_cb; + bsc_schedule_timer(&rate_ctr_timer, 1, 0); + + return 0; +} -- cgit v1.2.3 From 087fcff9a5342f7a10c9f378bd4130f409657001 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 13 May 2010 12:16:17 +0200 Subject: rate_ctr: Store the numeric index as part of 'rate_ctr_group' --- include/osmocore/rate_ctr.h | 1 + src/rate_ctr.c | 1 + 2 files changed, 2 insertions(+) diff --git a/include/osmocore/rate_ctr.h b/include/osmocore/rate_ctr.h index c6a1ace2..47fd5a36 100644 --- a/include/osmocore/rate_ctr.h +++ b/include/osmocore/rate_ctr.h @@ -49,6 +49,7 @@ struct rate_ctr_group { struct llist_head list; /* Pointer to the counter group class */ const struct rate_ctr_group_desc *desc; + unsigned int idx; /* The name prefix generated from desc->group_prefix_fmt and index */ char *name_prefix; /* Actual counter structures below */ diff --git a/src/rate_ctr.c b/src/rate_ctr.c index 9b2459e8..bde090a4 100644 --- a/src/rate_ctr.c +++ b/src/rate_ctr.c @@ -51,6 +51,7 @@ struct rate_ctr_group *rate_ctr_group_alloc(void *ctx, return NULL; group->desc = desc; + group->idx = idx; /* Generate the Group prefix from the user-specified index */ group->name_prefix = talloc_size(group, strlen(desc->group_prefix_fmt) + 20); sprintf(group->name_prefix, desc->group_prefix_fmt, idx); -- cgit v1.2.3 From dd178b2dc9a34aaa28ee00300db9d5ef6d45e820 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 13 May 2010 12:50:44 +0200 Subject: rate_counters: Remove group-name-sprintf-with-idx string --- include/osmocore/rate_ctr.h | 3 +-- src/rate_ctr.c | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/include/osmocore/rate_ctr.h b/include/osmocore/rate_ctr.h index 47fd5a36..85fe5c1f 100644 --- a/include/osmocore/rate_ctr.h +++ b/include/osmocore/rate_ctr.h @@ -49,9 +49,8 @@ struct rate_ctr_group { struct llist_head list; /* Pointer to the counter group class */ const struct rate_ctr_group_desc *desc; + /* The index of this ctr_group within its class */ unsigned int idx; - /* The name prefix generated from desc->group_prefix_fmt and index */ - char *name_prefix; /* Actual counter structures below */ struct rate_ctr ctr[0]; }; diff --git a/src/rate_ctr.c b/src/rate_ctr.c index bde090a4..e48c7792 100644 --- a/src/rate_ctr.c +++ b/src/rate_ctr.c @@ -52,9 +52,6 @@ struct rate_ctr_group *rate_ctr_group_alloc(void *ctx, group->desc = desc; group->idx = idx; - /* Generate the Group prefix from the user-specified index */ - group->name_prefix = talloc_size(group, strlen(desc->group_prefix_fmt) + 20); - sprintf(group->name_prefix, desc->group_prefix_fmt, idx); llist_add(&group->list, &rate_ctr_groups); -- cgit v1.2.3 From cf734784b0433dfa6b77909f83cc3620e523f5d7 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 13 May 2010 12:53:35 +0200 Subject: [rate_ctr] Rename group_prefix_fmt to group_name_prefix --- include/osmocore/rate_ctr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/osmocore/rate_ctr.h b/include/osmocore/rate_ctr.h index 85fe5c1f..88c9de5a 100644 --- a/include/osmocore/rate_ctr.h +++ b/include/osmocore/rate_ctr.h @@ -33,8 +33,8 @@ struct rate_ctr_desc { /* Describe a counter group class */ struct rate_ctr_group_desc { - /* The prefix / format string to be used for all counters */ - char *group_prefix_fmt; + /* The prefix to the name of all counters in this group */ + char *group_name_prefix; /* The human-readable description of the group */ char *group_description; /* The number of counters in this group */ -- cgit v1.2.3 From d2dce6df04c859e33a79685640ad0a295dcc1421 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 13 May 2010 13:28:12 +0200 Subject: [rate_ctr] always 'overflow' in next larger inetrval when interval ends If a second ends, we add the number of events in that just-ended second to the number of events in the currently running minute. The same happens at the end of a minute: We add the number of events in that just-ended minute into the number of events of the still-running hour, etc. This gives a much more meaningful numbers and we don't end up with "12 events per second, but 0 events per minute" kind of situations anymore. --- src/rate_ctr.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rate_ctr.c b/src/rate_ctr.c index e48c7792..f58b5c4a 100644 --- a/src/rate_ctr.c +++ b/src/rate_ctr.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -75,6 +76,11 @@ static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv) ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last; /* save current counter for next interval */ ctr->intv[intv].last = ctr->current; + + /* update the rate of the next bigger interval. This will + * be overwritten when that next larger interval expires */ + if (intv + 1 < ARRAY_SIZE(ctr->intv)) + ctr->intv[intv+1].rate += ctr->intv[intv].rate; } static struct timer_list rate_ctr_timer; -- cgit v1.2.3 From 102bcafb58c18a0aacaa8952bdee83460a1669dd Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Fri, 14 May 2010 07:25:01 +0800 Subject: gsm0808: Add TLV entries for types used by the Compl Layer3 Information --- src/gsm0808.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gsm0808.c b/src/gsm0808.c index 7a7eb3a0..114ddbf6 100644 --- a/src/gsm0808.c +++ b/src/gsm0808.c @@ -286,6 +286,9 @@ static const struct tlv_definition bss_att_tlvdef = { [GSM0808_IE_SERVICE_HANDOVER] = { TLV_TYPE_TV}, [GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV }, [GSM0808_IE_CIPHER_RESPONSE_MODE] = { TLV_TYPE_TV }, + [GSM0808_IE_CELL_IDENTIFIER] = { TLV_TYPE_TLV }, + [GSM0808_IE_CHOSEN_CHANNEL] = { TLV_TYPE_TV }, + [GSM0808_IE_LAYER_3_INFORMATION] = { TLV_TYPE_TLV }, }, }; -- cgit v1.2.3 From 5dcef3c8b109bd2ff41cdf6ecf84e624291892e1 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Sat, 15 May 2010 22:35:25 +0800 Subject: gsm48: Add paging response header --- include/osmocore/protocol/gsm_04_08.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/osmocore/protocol/gsm_04_08.h b/include/osmocore/protocol/gsm_04_08.h index 1a112a08..20023787 100644 --- a/include/osmocore/protocol/gsm_04_08.h +++ b/include/osmocore/protocol/gsm_04_08.h @@ -120,6 +120,15 @@ struct gsm48_imm_ass { uint8_t mob_alloc[0]; } __attribute__ ((packed)); +/* Chapter 9.1.25 */ +struct gsm48_pag_resp { + uint8_t spare:4, + key_seq:4; + uint32_t classmark2; + uint8_t mi_len; + uint8_t mi[0]; +} __attribute__ ((packed)); + /* Chapter 10.5.1.3 */ struct gsm48_loc_area_id { uint8_t digits[3]; /* BCD! */ -- cgit v1.2.3 From 9a3dec0c01c67fd6ee10e499fc2abb139d077a2c Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Sun, 16 May 2010 08:15:40 +0800 Subject: gsm0808: Add API to wrap a GSM48 msg with a BSSMAP/DTAP msg. --- include/osmocore/gsm0808.h | 2 ++ src/gsm0808.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/include/osmocore/gsm0808.h b/include/osmocore/gsm0808.h index a40713f7..9166e54f 100644 --- a/include/osmocore/gsm0808.h +++ b/include/osmocore/gsm0808.h @@ -36,6 +36,8 @@ struct msgb *gsm0808_create_assignment_completed(struct gsm_lchan *lchan, uint8_ uint8_t speech_mode); struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause); +void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id); + const struct tlv_definition *gsm0808_att_tlvdef(); #endif diff --git a/src/gsm0808.c b/src/gsm0808.c index 114ddbf6..1dc035b3 100644 --- a/src/gsm0808.c +++ b/src/gsm0808.c @@ -266,6 +266,14 @@ struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause) return msg; } +void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id) +{ + uint8_t *hh = msgb_push(msg, 3); + hh[0] = BSSAP_MSG_DTAP; + hh[1] = link_id; + hh[2] = msg->len - 3; +} + static const struct tlv_definition bss_att_tlvdef = { .def = { [GSM0808_IE_IMSI] = { TLV_TYPE_TLV }, -- cgit v1.2.3 From 0ebd688746f2e8e6954b057460f53ae32b4f6e42 Mon Sep 17 00:00:00 2001 From: "Andreas.Eversberg" Date: Sun, 9 May 2010 09:36:54 +0000 Subject: Added some "get"-functions to bitvec of libosmocore. - Getting Low or High value from current postion - Getting unsigned interger from current postion --- include/osmocore/bitvec.h | 10 ++++++++++ src/bitvec.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/include/osmocore/bitvec.h b/include/osmocore/bitvec.h index 7a26bce4..42977fb2 100644 --- a/include/osmocore/bitvec.h +++ b/include/osmocore/bitvec.h @@ -42,6 +42,10 @@ struct bitvec { /* check if the bit is 0 or 1 for a given position inside a bitvec */ enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr); +/* check if the bit is L or H for a given position inside a bitvec */ +enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv, + unsigned int bitnr); + /* get the Nth set bit inside the bit vector */ unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n); @@ -52,12 +56,18 @@ int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum, /* Set the next bit in the vector */ int bitvec_set_bit(struct bitvec *bv, enum bit_value bit); +/* get the next bit (low/high) inside a bitvec */ +int bitvec_get_bit_high(struct bitvec *bv); + /* Set multiple bits at the current position */ int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count); /* Add an unsigned integer (of length count bits) to current position */ int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count); +/* get multiple bits (based on numeric value) from current pos */ +int bitvec_get_uint(struct bitvec *bv, int num_bits); + /* Pad the bit vector up to a certain bit position */ int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit); diff --git a/src/bitvec.c b/src/bitvec.c index eb83ac66..04c465a8 100644 --- a/src/bitvec.c +++ b/src/bitvec.c @@ -77,6 +77,25 @@ enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr) return ZERO; } +/* check if the bit is L or H for a given position inside a bitvec */ +enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv, + unsigned int bitnr) +{ + unsigned int bytenum = bytenum_from_bitnum(bitnr); + unsigned int bitnum = 7 - (bitnr % 8); + uint8_t bitval; + + if (bytenum >= bv->data_len) + return -EINVAL; + + bitval = bitval2mask(H, bitnum); + + if (bv->data[bytenum] & bitval) + return H; + + return L; +} + /* get the Nth set bit inside the bit vector */ unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n) { @@ -127,6 +146,18 @@ int bitvec_set_bit(struct bitvec *bv, enum bit_value bit) return rc; } +/* get the next bit (low/high) inside a bitvec */ +int bitvec_get_bit_high(struct bitvec *bv) +{ + int rc; + + rc = bitvec_get_bit_pos_high(bv, bv->cur_bit); + if (rc >= 0) + bv->cur_bit++; + + return rc; +} + /* set multiple bits (based on array of bitvals) at current pos */ int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count) { @@ -158,6 +189,24 @@ int bitvec_set_uint(struct bitvec *bv, unsigned int ui, int num_bits) return 0; } +/* get multiple bits (based on numeric value) from current pos */ +int bitvec_get_uint(struct bitvec *bv, int num_bits) +{ + int i; + unsigned int ui = 0; + + for (i = 0; i < num_bits; i++) { + int bit = bitvec_get_bit_pos(bv, bv->cur_bit); + if (bit < 0) + return bit; + if (bit) + ui |= (1 << (num_bits - i - 1)); + bv->cur_bit++; + } + + return ui; +} + /* pad all remaining bits up to num_bits */ int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit) { -- cgit v1.2.3 From 75bd69bc914d4ecd1b19f65662889af173aca64c Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Sun, 23 May 2010 21:14:32 +0800 Subject: rate_ctr: Make the struct rate_ctr_group_desc members const --- include/osmocore/rate_ctr.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/osmocore/rate_ctr.h b/include/osmocore/rate_ctr.h index 88c9de5a..f887d9a7 100644 --- a/include/osmocore/rate_ctr.h +++ b/include/osmocore/rate_ctr.h @@ -34,13 +34,13 @@ struct rate_ctr_desc { /* Describe a counter group class */ struct rate_ctr_group_desc { /* The prefix to the name of all counters in this group */ - char *group_name_prefix; + const char *group_name_prefix; /* The human-readable description of the group */ - char *group_description; + const char *group_description; /* The number of counters in this group */ - unsigned int num_ctr; + const unsigned int num_ctr; /* Pointer to array of counter names */ - struct rate_ctr_desc *ctr_desc; + const struct rate_ctr_desc *ctr_desc; }; /* One instance of a counter group class */ -- cgit v1.2.3 From 3fb0b6f26e24370490f1051e2c795ba8fa6ac892 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 19 May 2010 19:02:52 +0200 Subject: Create libosmovty as library from OpenBSC VTY functions --- configure.in | 3 + include/Makefile.am | 2 +- include/osmocom/Makefile.am | 1 + include/osmocom/vty/Makefile.am | 2 + include/osmocom/vty/buffer.h | 102 + include/osmocom/vty/command.h | 392 ++++ include/osmocom/vty/logging.h | 7 + include/osmocom/vty/telnet_interface.h | 40 + include/osmocom/vty/vector.h | 64 + include/osmocom/vty/vty.h | 151 ++ src/Makefile.am | 2 + src/logging.c | 2 +- src/vty/Makefile.am | 11 + src/vty/buffer.c | 463 +++++ src/vty/command.c | 3176 ++++++++++++++++++++++++++++++++ src/vty/logging_vty.c | 347 ++++ src/vty/telnet_interface.c | 203 ++ src/vty/utils.c | 50 + src/vty/vector.c | 192 ++ src/vty/vty.c | 1685 +++++++++++++++++ 20 files changed, 6893 insertions(+), 2 deletions(-) create mode 100644 include/osmocom/Makefile.am create mode 100644 include/osmocom/vty/Makefile.am create mode 100644 include/osmocom/vty/buffer.h create mode 100644 include/osmocom/vty/command.h create mode 100644 include/osmocom/vty/logging.h create mode 100644 include/osmocom/vty/telnet_interface.h create mode 100644 include/osmocom/vty/vector.h create mode 100644 include/osmocom/vty/vty.h create mode 100644 src/vty/Makefile.am create mode 100644 src/vty/buffer.c create mode 100644 src/vty/command.c create mode 100644 src/vty/logging_vty.c create mode 100644 src/vty/telnet_interface.c create mode 100644 src/vty/utils.c create mode 100644 src/vty/vector.c create mode 100644 src/vty/vty.c diff --git a/configure.in b/configure.in index 9879ebc0..abf098cc 100644 --- a/configure.in +++ b/configure.in @@ -46,10 +46,13 @@ AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "x1") AC_OUTPUT( libosmocore.pc + include/osmocom/Makefile + include/osmocom/vty/Makefile include/osmocore/Makefile include/osmocore/protocol/Makefile include/Makefile src/Makefile + src/vty/Makefile tests/Makefile tests/timer/Makefile tests/sms/Makefile diff --git a/include/Makefile.am b/include/Makefile.am index f0015d5f..185c6968 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1 +1 @@ -SUBDIRS = osmocore +SUBDIRS = osmocom osmocore diff --git a/include/osmocom/Makefile.am b/include/osmocom/Makefile.am new file mode 100644 index 00000000..c9023e65 --- /dev/null +++ b/include/osmocom/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = vty diff --git a/include/osmocom/vty/Makefile.am b/include/osmocom/vty/Makefile.am new file mode 100644 index 00000000..e51dcb83 --- /dev/null +++ b/include/osmocom/vty/Makefile.am @@ -0,0 +1,2 @@ +noinst_HEADERS = buffer.h command.h vector.h vty.h \ + telnet_interface.h diff --git a/include/osmocom/vty/buffer.h b/include/osmocom/vty/buffer.h new file mode 100644 index 00000000..c9467a91 --- /dev/null +++ b/include/osmocom/vty/buffer.h @@ -0,0 +1,102 @@ +/* + * Buffering to output and input. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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 2, or (at your + * option) any later version. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _ZEBRA_BUFFER_H +#define _ZEBRA_BUFFER_H + +#include + +/* Create a new buffer. Memory will be allocated in chunks of the given + size. If the argument is 0, the library will supply a reasonable + default size suitable for buffering socket I/O. */ +struct buffer *buffer_new(void *ctx, size_t); + +/* Free all data in the buffer. */ +void buffer_reset(struct buffer *); + +/* This function first calls buffer_reset to release all buffered data. + Then it frees the struct buffer itself. */ +void buffer_free(struct buffer *); + +/* Add the given data to the end of the buffer. */ +extern void buffer_put(struct buffer *, const void *, size_t); +/* Add a single character to the end of the buffer. */ +extern void buffer_putc(struct buffer *, u_char); +/* Add a NUL-terminated string to the end of the buffer. */ +extern void buffer_putstr(struct buffer *, const char *); + +/* Combine all accumulated (and unflushed) data inside the buffer into a + single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note + that this function does not alter the state of the buffer, so the data + is still inside waiting to be flushed. */ +char *buffer_getstr(struct buffer *); + +/* Returns 1 if there is no pending data in the buffer. Otherwise returns 0. */ +int buffer_empty(struct buffer *); + +typedef enum { + /* An I/O error occurred. The buffer should be destroyed and the + file descriptor should be closed. */ + BUFFER_ERROR = -1, + + /* The data was written successfully, and the buffer is now empty + (there is no pending data waiting to be flushed). */ + BUFFER_EMPTY = 0, + + /* There is pending data in the buffer waiting to be flushed. Please + try flushing the buffer when select indicates that the file descriptor + is writeable. */ + BUFFER_PENDING = 1 +} buffer_status_t; + +/* Try to write this data to the file descriptor. Any data that cannot + be written immediately is added to the buffer queue. */ +extern buffer_status_t buffer_write(struct buffer *, int fd, + const void *, size_t); + +/* This function attempts to flush some (but perhaps not all) of + the queued data to the given file descriptor. */ +extern buffer_status_t buffer_flush_available(struct buffer *, int fd); + +/* The following 2 functions (buffer_flush_all and buffer_flush_window) + are for use in lib/vty.c only. They should not be used elsewhere. */ + +/* Call buffer_flush_available repeatedly until either all data has been + flushed, or an I/O error has been encountered, or the operation would + block. */ +extern buffer_status_t buffer_flush_all(struct buffer *, int fd); + +/* Attempt to write enough data to the given fd to fill a window of the + given width and height (and remove the data written from the buffer). + + If !no_more, then a message saying " --More-- " is appended. + If erase is true, then first overwrite the previous " --More-- " message + with spaces. + + Any write error (including EAGAIN or EINTR) will cause this function + to return -1 (because the logic for handling the erase and more features + is too complicated to retry the write later). +*/ +extern buffer_status_t buffer_flush_window(struct buffer *, int fd, int width, + int height, int erase, int no_more); + +#endif /* _ZEBRA_BUFFER_H */ diff --git a/include/osmocom/vty/command.h b/include/osmocom/vty/command.h new file mode 100644 index 00000000..1db8ffea --- /dev/null +++ b/include/osmocom/vty/command.h @@ -0,0 +1,392 @@ +/* + * Zebra configuration command interface routine + * Copyright (C) 1997, 98 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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 2, or (at your + * option) any later version. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _ZEBRA_COMMAND_H +#define _ZEBRA_COMMAND_H + +#include +#include +#include "vector.h" +#include "vty.h" + +/* Host configuration variable */ +struct host { + /* Host name of this router. */ + char *name; + + /* Password for vty interface. */ + char *password; + char *password_encrypt; + + /* Enable password */ + char *enable; + char *enable_encrypt; + + /* System wide terminal lines. */ + int lines; + + /* Log filename. */ + char *logfile; + + /* config file name of this host */ + char *config; + + /* Flags for services */ + int advanced; + int encrypt; + + /* Banner configuration. */ + const char *motd; + char *motdfile; + + const char *prog_name; + const char *prog_version; + const char *prog_copyright; +}; + +/* There are some command levels which called from command node. */ +enum node_type { + AUTH_NODE, /* Authentication mode of vty interface. */ + VIEW_NODE, /* View node. Default mode of vty interface. */ + AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ + ENABLE_NODE, /* Enable node. */ + CONFIG_NODE, /* Config node. Default mode of config file. */ + SERVICE_NODE, /* Service node. */ + DEBUG_NODE, /* Debug node. */ +#if 0 + AAA_NODE, /* AAA node. */ + KEYCHAIN_NODE, /* Key-chain node. */ + KEYCHAIN_KEY_NODE, /* Key-chain key node. */ + INTERFACE_NODE, /* Interface mode node. */ + ZEBRA_NODE, /* zebra connection node. */ + TABLE_NODE, /* rtm_table selection node. */ + RIP_NODE, /* RIP protocol mode node. */ + RIPNG_NODE, /* RIPng protocol mode node. */ + BGP_NODE, /* BGP protocol mode which includes BGP4+ */ + BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ + BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ + BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ + BGP_IPV6_NODE, /* BGP IPv6 address family */ + OSPF_NODE, /* OSPF protocol mode */ + OSPF6_NODE, /* OSPF protocol for IPv6 mode */ + ISIS_NODE, /* ISIS protocol mode */ + MASC_NODE, /* MASC for multicast. */ + IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ + IP_NODE, /* Static ip route node. */ + ACCESS_NODE, /* Access list node. */ + PREFIX_NODE, /* Prefix list node. */ + ACCESS_IPV6_NODE, /* Access list node. */ + PREFIX_IPV6_NODE, /* Prefix list node. */ + AS_LIST_NODE, /* AS list node. */ + COMMUNITY_LIST_NODE, /* Community list node. */ + RMAP_NODE, /* Route map node. */ + SMUX_NODE, /* SNMP configuration node. */ + DUMP_NODE, /* Packet dump node. */ + FORWARDING_NODE, /* IP forwarding node. */ +#endif + VTY_NODE, /* Vty node. */ + + GSMNET_NODE, + BTS_NODE, + TRX_NODE, + TS_NODE, + SUBSCR_NODE, + MGCP_NODE, + GBPROXY_NODE, + SGSN_NODE, + NS_NODE, + BSSGP_NODE, +}; + +/* Node which has some commands and prompt string and configuration + function pointer . */ +struct cmd_node { + /* Node index. */ + enum node_type node; + + /* Prompt character at vty interface. */ + const char *prompt; + + /* Is this node's configuration goes to vtysh ? */ + int vtysh; + + /* Node's configuration write function */ + int (*func) (struct vty *); + + /* Vector of this node's command list. */ + vector cmd_vector; +}; + +enum { + CMD_ATTR_DEPRECATED = 1, + CMD_ATTR_HIDDEN, +}; + +/* Structure of command element. */ +struct cmd_element { + const char *string; /* Command specification by string. */ + int (*func) (struct cmd_element *, struct vty *, int, const char *[]); + const char *doc; /* Documentation of this command. */ + int daemon; /* Daemon to which this command belong. */ + vector strvec; /* Pointing out each description vector. */ + unsigned int cmdsize; /* Command index count. */ + char *config; /* Configuration string */ + vector subconfig; /* Sub configuration string */ + u_char attr; /* Command attributes */ +}; + +/* Command description structure. */ +struct desc { + const char *cmd; /* Command string. */ + const char *str; /* Command's description. */ +}; + +/* Return value of the commands. */ +#define CMD_SUCCESS 0 +#define CMD_WARNING 1 +#define CMD_ERR_NO_MATCH 2 +#define CMD_ERR_AMBIGUOUS 3 +#define CMD_ERR_INCOMPLETE 4 +#define CMD_ERR_EXEED_ARGC_MAX 5 +#define CMD_ERR_NOTHING_TODO 6 +#define CMD_COMPLETE_FULL_MATCH 7 +#define CMD_COMPLETE_MATCH 8 +#define CMD_COMPLETE_LIST_MATCH 9 +#define CMD_SUCCESS_DAEMON 10 + +/* Argc max counts. */ +#define CMD_ARGC_MAX 25 + +/* Turn off these macros when uisng cpp with extract.pl */ +#ifndef VTYSH_EXTRACT_PL + +/* helper defines for end-user DEFUN* macros */ +#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ + static struct cmd_element cmdname = \ + { \ + .string = cmdstr, \ + .func = funcname, \ + .doc = helpstr, \ + .attr = attrs, \ + .daemon = dnum, \ + }; + +/* global (non static) cmd_element */ +#define gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ + struct cmd_element cmdname = \ + { \ + .string = cmdstr, \ + .func = funcname, \ + .doc = helpstr, \ + .attr = attrs, \ + .daemon = dnum, \ + }; + +#define DEFUN_CMD_FUNC_DECL(funcname) \ + static int funcname (struct cmd_element *, struct vty *, int, const char *[]); \ + +#define DEFUN_CMD_FUNC_TEXT(funcname) \ + static int funcname \ + (struct cmd_element *self, struct vty *vty, int argc, const char *argv[]) + +/* DEFUN for vty command interafce. Little bit hacky ;-). */ +#define DEFUN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +/* global (non static) cmd_element */ +#define gDEFUN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) + +#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \ + +/* DEFUN_NOSH for commands that vtysh should ignore */ +#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \ + DEFUN(funcname, cmdname, cmdstr, helpstr) + +/* DEFSH for vtysh. */ +#define DEFSH(daemon, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \ + +/* DEFUN + DEFSH */ +#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +/* DEFUN + DEFSH with attributes */ +#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) + +#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) + +/* ALIAS macro which define existing command's alias. */ +#define ALIAS(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) + +/* global (non static) cmd_element */ +#define gALIAS(funcname, cmdname, cmdstr, helpstr) \ + gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) + +#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) + +#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0) + +#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0) + +#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) + +#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon) + +#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon) + +#endif /* VTYSH_EXTRACT_PL */ + +/* Some macroes */ +#define CMD_OPTION(S) ((S[0]) == '[') +#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<')) +#define CMD_VARARG(S) ((S[0]) == '.') +#define CMD_RANGE(S) ((S[0] == '<')) + +#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0)) +#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0)) +#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0)) +#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0)) + +/* Common descriptions. */ +#define SHOW_STR "Show running system information\n" +#define IP_STR "IP information\n" +#define IPV6_STR "IPv6 information\n" +#define NO_STR "Negate a command or set its defaults\n" +#define CLEAR_STR "Reset functions\n" +#define RIP_STR "RIP information\n" +#define BGP_STR "BGP information\n" +#define OSPF_STR "OSPF information\n" +#define NEIGHBOR_STR "Specify neighbor router\n" +#define DEBUG_STR "Debugging functions (see also 'undebug')\n" +#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n" +#define ROUTER_STR "Enable a routing process\n" +#define AS_STR "AS number\n" +#define MBGP_STR "MBGP information\n" +#define MATCH_STR "Match values from routing table\n" +#define SET_STR "Set values in destination routing protocol\n" +#define OUT_STR "Filter outgoing routing updates\n" +#define IN_STR "Filter incoming routing updates\n" +#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n" +#define OSPF6_NUMBER_STR "Specify by number\n" +#define INTERFACE_STR "Interface infomation\n" +#define IFNAME_STR "Interface name(e.g. ep0)\n" +#define IP6_STR "IPv6 Information\n" +#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n" +#define OSPF6_ROUTER_STR "Enable a routing process\n" +#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n" +#define SECONDS_STR "<1-65535> Seconds\n" +#define ROUTE_STR "Routing Table\n" +#define PREFIX_LIST_STR "Build a prefix list\n" +#define OSPF6_DUMP_TYPE_LIST \ +"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)" +#define ISIS_STR "IS-IS information\n" +#define AREA_TAG_STR "[area tag]\n" + +#define CONF_BACKUP_EXT ".sav" + +/* IPv4 only machine should not accept IPv6 address for peer's IP + address. So we replace VTY command string like below. */ +#ifdef HAVE_IPV6 +#define NEIGHBOR_CMD "neighbor (A.B.C.D|X:X::X:X) " +#define NO_NEIGHBOR_CMD "no neighbor (A.B.C.D|X:X::X:X) " +#define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n" +#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|X:X::X:X|WORD) " +#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|X:X::X:X|WORD) " +#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nNeighbor tag\n" +#else +#define NEIGHBOR_CMD "neighbor A.B.C.D " +#define NO_NEIGHBOR_CMD "no neighbor A.B.C.D " +#define NEIGHBOR_ADDR_STR "Neighbor address\n" +#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|WORD) " +#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|WORD) " +#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n" +#endif /* HAVE_IPV6 */ + +/* Prototypes. */ +void install_node(struct cmd_node *, int (*)(struct vty *)); +void install_default(enum node_type); +void install_element(enum node_type, struct cmd_element *); +void install_element_ve(struct cmd_element *cmd); +void sort_node(); + +/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated + string with a space between each element (allocated using + XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */ +char *argv_concat(const char **argv, int argc, int shift); + +vector cmd_make_strvec(const char *); +void cmd_free_strvec(vector); +vector cmd_describe_command(); +char **cmd_complete_command(); +const char *cmd_prompt(enum node_type); +int config_from_file(struct vty *, FILE *); +enum node_type node_parent(enum node_type); +int cmd_execute_command(vector, struct vty *, struct cmd_element **, int); +int cmd_execute_command_strict(vector, struct vty *, struct cmd_element **); +void config_replace_string(struct cmd_element *, char *, ...); +void cmd_init(int); + +/* Export typical functions. */ +extern struct cmd_element config_exit_cmd; +extern struct cmd_element config_help_cmd; +extern struct cmd_element config_list_cmd; +char *host_config_file(); +void host_config_set(const char *); + +/* This is called from main when a daemon is invoked with -v or --version. */ +void print_version(int print_copyright); + +extern void *tall_vty_cmd_ctx; + +enum node_type (*vty_go_parent_cb)(struct vty *vty); + +#endif /* _ZEBRA_COMMAND_H */ diff --git a/include/osmocom/vty/logging.h b/include/osmocom/vty/logging.h new file mode 100644 index 00000000..f8ffbc3e --- /dev/null +++ b/include/osmocom/vty/logging.h @@ -0,0 +1,7 @@ +#ifndef _VTY_LOGGING_H +#define _VTY_LOGGING_H + +#define LOGGING_STR "Configure log message to this terminal\n" +#define FILTER_STR "Filter log messages\n" + +#endif /* _VTY_LOGGING_H */ diff --git a/include/osmocom/vty/telnet_interface.h b/include/osmocom/vty/telnet_interface.h new file mode 100644 index 00000000..444e6497 --- /dev/null +++ b/include/osmocom/vty/telnet_interface.h @@ -0,0 +1,40 @@ +/* minimalistic telnet/network interface it might turn into a wire interface */ +/* (C) 2009 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef TELNET_INTERFACE_H +#define TELNET_INTERFACE_H + +#include +#include + +#include + +struct telnet_connection { + struct llist_head entry; + void *priv; + struct bsc_fd fd; + struct vty *vty; + struct log_target *dbg; +}; + + +int telnet_init(void *tall_ctx, void *priv, int port); + +#endif diff --git a/include/osmocom/vty/vector.h b/include/osmocom/vty/vector.h new file mode 100644 index 00000000..22a184d6 --- /dev/null +++ b/include/osmocom/vty/vector.h @@ -0,0 +1,64 @@ +/* + * Generic vector interface header. + * Copyright (C) 1997, 98 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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 2, or (at your option) any + * later version. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_VECTOR_H +#define _ZEBRA_VECTOR_H + +/* struct for vector */ +struct _vector { + unsigned int active; /* number of active slots */ + unsigned int alloced; /* number of allocated slot */ + void **index; /* index to data */ +}; +typedef struct _vector *vector; + +#define VECTOR_MIN_SIZE 1 + +/* (Sometimes) usefull macros. This macro convert index expression to + array expression. */ +/* Reference slot at given index, caller must ensure slot is active */ +#define vector_slot(V,I) ((V)->index[(I)]) +/* Number of active slots. + * Note that this differs from vector_count() as it the count returned + * will include any empty slots + */ +#define vector_active(V) ((V)->active) + +/* Prototypes. */ +vector vector_init(unsigned int size); +void vector_ensure(vector v, unsigned int num); +int vector_empty_slot(vector v); +int vector_set(vector v, void *val); +int vector_set_index(vector v, unsigned int i, void *val); +void vector_unset(vector v, unsigned int i); +unsigned int vector_count(vector v); +void vector_only_wrapper_free(vector v); +void vector_only_index_free(void *index); +void vector_free(vector v); +vector vector_copy(vector v); + +void *vector_lookup(vector, unsigned int); +void *vector_lookup_ensure(vector, unsigned int); + +extern void *tall_vty_vec_ctx; + +#endif /* _ZEBRA_VECTOR_H */ diff --git a/include/osmocom/vty/vty.h b/include/osmocom/vty/vty.h new file mode 100644 index 00000000..f82ada1c --- /dev/null +++ b/include/osmocom/vty/vty.h @@ -0,0 +1,151 @@ +#ifndef _VTY_H +#define _VTY_H + +#include +#include + +/* GCC have printf type attribute check. */ +#ifdef __GNUC__ +#define VTY_PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#else +#define VTY_PRINTF_ATTRIBUTE(a,b) +#endif /* __GNUC__ */ + +/* Does the I/O error indicate that the operation should be retried later? */ +#define ERRNO_IO_RETRY(EN) \ + (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR)) + +/* Vty read buffer size. */ +#define VTY_READ_BUFSIZ 512 + +#define VTY_BUFSIZ 512 +#define VTY_MAXHIST 20 + +/* Vty events */ +enum event { + VTY_SERV, + VTY_READ, + VTY_WRITE, + VTY_CLOSED, + VTY_TIMEOUT_RESET, +#ifdef VTYSH + VTYSH_SERV, + VTYSH_READ, + VTYSH_WRITE +#endif /* VTYSH */ +}; + +struct vty { + FILE *file; + + /* private data, specified by creator */ + void *priv; + + /* File descripter of this vty. */ + int fd; + + /* Is this vty connect to file or not */ + enum { VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV } type; + + /* Node status of this vty */ + int node; + + /* Failure count */ + int fail; + + /* Output buffer. */ + struct buffer *obuf; + + /* Command input buffer */ + char *buf; + + /* Command cursor point */ + int cp; + + /* Command length */ + int length; + + /* Command max length. */ + int max; + + /* Histry of command */ + char *hist[VTY_MAXHIST]; + + /* History lookup current point */ + int hp; + + /* History insert end point */ + int hindex; + + /* For current referencing point of interface, route-map, + access-list etc... */ + void *index; + + /* For multiple level index treatment such as key chain and key. */ + void *index_sub; + + /* For escape character. */ + unsigned char escape; + + /* Current vty status. */ + enum { VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE } status; + + /* IAC handling: was the last character received the IAC + * (interpret-as-command) escape character (and therefore the next + * character will be the command code)? Refer to Telnet RFC 854. */ + unsigned char iac; + + /* IAC SB (option subnegotiation) handling */ + unsigned char iac_sb_in_progress; + /* At the moment, we care only about the NAWS (window size) negotiation, + * and that requires just a 5-character buffer (RFC 1073): + * <16-bit width> <16-bit height> */ +#define TELNET_NAWS_SB_LEN 5 + unsigned char sb_buf[TELNET_NAWS_SB_LEN]; + /* How many subnegotiation characters have we received? We just drop + * those that do not fit in the buffer. */ + size_t sb_len; + + /* Window width/height. */ + int width; + int height; + + /* Configure lines. */ + int lines; + + int monitor; + + /* In configure mode. */ + int config; +}; + +/* Small macro to determine newline is newline only or linefeed needed. */ +#define VTY_NEWLINE ((vty->type == VTY_TERM) ? "\r\n" : "\n") + +static inline char *vty_newline(struct vty *vty) +{ + return VTY_NEWLINE; +} + +/* Prototypes. */ +void vty_init(const char *name, const char *version, const char *copyright); +int vty_read_config_file(const char *file_name, void *priv); +void vty_init_vtysh (void); +void vty_reset (void); +struct vty *vty_new (void); +struct vty *vty_create (int vty_sock, void *priv); +int vty_out (struct vty *, const char *, ...) VTY_PRINTF_ATTRIBUTE(2, 3); +int vty_out_newline(struct vty *); +int vty_read(struct vty *vty); +//void vty_time_print (struct vty *, int); +void vty_close (struct vty *); +char *vty_get_cwd (void); +void vty_log (const char *level, const char *proto, const char *fmt, va_list); +int vty_config_lock (struct vty *); +int vty_config_unlock (struct vty *); +int vty_shell (struct vty *); +int vty_shell_serv (struct vty *); +void vty_hello (struct vty *); + +void *tall_vty_ctx; +#endif diff --git a/src/Makefile.am b/src/Makefile.am index ce063d05..2e2eaf0e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,3 +1,5 @@ +SUBDRIS=vty + # This is _NOT_ the library release version, it's an API version. # Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification LIBVERSION=0:0:0 diff --git a/src/logging.c b/src/logging.c index e72a6e20..531cea6c 100644 --- a/src/logging.c +++ b/src/logging.c @@ -37,7 +37,7 @@ #include #include -static const struct log_info *log_info; +const struct log_info *log_info; static struct log_context log_context; static void *tall_log_ctx = NULL; diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am new file mode 100644 index 00000000..bc10a0f8 --- /dev/null +++ b/src/vty/Makefile.am @@ -0,0 +1,11 @@ +# This is _NOT_ the library release version, it's an API version. +# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification +LIBVERSION=0:0:0 + +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS = -fPIC -Wall + +lib_LTLIBRARIES = libosmovty.la + +libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \ + telnet_interface.c logging_vty.c diff --git a/src/vty/buffer.c b/src/vty/buffer.c new file mode 100644 index 00000000..a5655b93 --- /dev/null +++ b/src/vty/buffer.c @@ -0,0 +1,463 @@ +/* + * Buffering of output and input. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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 2, or (at your + * option) any later version. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Buffer master. */ +struct buffer { + /* Data list. */ + struct buffer_data *head; + struct buffer_data *tail; + + /* Size of each buffer_data chunk. */ + size_t size; +}; + +/* Data container. */ +struct buffer_data { + struct buffer_data *next; + + /* Location to add new data. */ + size_t cp; + + /* Pointer to data not yet flushed. */ + size_t sp; + + /* Actual data stream (variable length). */ + unsigned char data[0]; /* real dimension is buffer->size */ +}; + +/* It should always be true that: 0 <= sp <= cp <= size */ + +/* Default buffer size (used if none specified). It is rounded up to the + next page boundery. */ +#define BUFFER_SIZE_DEFAULT 4096 + +#define BUFFER_DATA_FREE(D) talloc_free((D)) + +/* Make new buffer. */ +struct buffer *buffer_new(void *ctx, size_t size) +{ + struct buffer *b; + + b = talloc_zero(ctx, struct buffer); + + if (size) + b->size = size; + else { + static size_t default_size; + if (!default_size) { + long pgsz = sysconf(_SC_PAGESIZE); + default_size = + ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz); + } + b->size = default_size; + } + + return b; +} + +/* Free buffer. */ +void buffer_free(struct buffer *b) +{ + buffer_reset(b); + talloc_free(b); +} + +/* Make string clone. */ +char *buffer_getstr(struct buffer *b) +{ + size_t totlen = 0; + struct buffer_data *data; + char *s; + char *p; + + for (data = b->head; data; data = data->next) + totlen += data->cp - data->sp; + if (!(s = _talloc_zero(tall_vty_ctx, (totlen + 1), "buffer_getstr"))) + return NULL; + p = s; + for (data = b->head; data; data = data->next) { + memcpy(p, data->data + data->sp, data->cp - data->sp); + p += data->cp - data->sp; + } + *p = '\0'; + return s; +} + +/* Return 1 if buffer is empty. */ +int buffer_empty(struct buffer *b) +{ + return (b->head == NULL); +} + +/* Clear and free all allocated data. */ +void buffer_reset(struct buffer *b) +{ + struct buffer_data *data; + struct buffer_data *next; + + for (data = b->head; data; data = next) { + next = data->next; + BUFFER_DATA_FREE(data); + } + b->head = b->tail = NULL; +} + +/* Add buffer_data to the end of buffer. */ +static struct buffer_data *buffer_add(struct buffer *b) +{ + struct buffer_data *d; + + d = _talloc_zero(b, + offsetof(struct buffer_data, data[b->size]), + "buffer_add"); + if (!d) + return NULL; + d->cp = d->sp = 0; + d->next = NULL; + + if (b->tail) + b->tail->next = d; + else + b->head = d; + b->tail = d; + + return d; +} + +/* Write data to buffer. */ +void buffer_put(struct buffer *b, const void *p, size_t size) +{ + struct buffer_data *data = b->tail; + const char *ptr = p; + + /* We use even last one byte of data buffer. */ + while (size) { + size_t chunk; + + /* If there is no data buffer add it. */ + if (data == NULL || data->cp == b->size) + data = buffer_add(b); + + chunk = + ((size <= + (b->size - data->cp)) ? size : (b->size - data->cp)); + memcpy((data->data + data->cp), ptr, chunk); + size -= chunk; + ptr += chunk; + data->cp += chunk; + } +} + +/* Insert character into the buffer. */ +void buffer_putc(struct buffer *b, u_char c) +{ + buffer_put(b, &c, 1); +} + +/* Put string to the buffer. */ +void buffer_putstr(struct buffer *b, const char *c) +{ + buffer_put(b, c, strlen(c)); +} + +/* Keep flushing data to the fd until the buffer is empty or an error is + encountered or the operation would block. */ +buffer_status_t buffer_flush_all(struct buffer *b, int fd) +{ + buffer_status_t ret; + struct buffer_data *head; + size_t head_sp; + + if (!b->head) + return BUFFER_EMPTY; + head_sp = (head = b->head)->sp; + /* Flush all data. */ + while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) { + if ((b->head == head) && (head_sp == head->sp) + && (errno != EINTR)) + /* No data was flushed, so kernel buffer must be full. */ + return ret; + head_sp = (head = b->head)->sp; + } + + return ret; +} + +#if 0 +/* Flush enough data to fill a terminal window of the given scene (used only + by vty telnet interface). */ +buffer_status_t +buffer_flush_window(struct buffer * b, int fd, int width, int height, + int erase_flag, int no_more_flag) +{ + int nbytes; + int iov_alloc; + int iov_index; + struct iovec *iov; + struct iovec small_iov[3]; + char more[] = " --More-- "; + char erase[] = + { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 + }; + struct buffer_data *data; + int column; + + if (!b->head) + return BUFFER_EMPTY; + + if (height < 1) { + zlog_warn + ("%s called with non-positive window height %d, forcing to 1", + __func__, height); + height = 1; + } else if (height >= 2) + height--; + if (width < 1) { + zlog_warn + ("%s called with non-positive window width %d, forcing to 1", + __func__, width); + width = 1; + } + + /* For erase and more data add two to b's buffer_data count. */ + if (b->head->next == NULL) { + iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]); + iov = small_iov; + } else { + iov_alloc = ((height * (width + 2)) / b->size) + 10; + iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov)); + } + iov_index = 0; + + /* Previously print out is performed. */ + if (erase_flag) { + iov[iov_index].iov_base = erase; + iov[iov_index].iov_len = sizeof erase; + iov_index++; + } + + /* Output data. */ + column = 1; /* Column position of next character displayed. */ + for (data = b->head; data && (height > 0); data = data->next) { + size_t cp; + + cp = data->sp; + while ((cp < data->cp) && (height > 0)) { + /* Calculate lines remaining and column position after displaying + this character. */ + if (data->data[cp] == '\r') + column = 1; + else if ((data->data[cp] == '\n') || (column == width)) { + column = 1; + height--; + } else + column++; + cp++; + } + iov[iov_index].iov_base = (char *)(data->data + data->sp); + iov[iov_index++].iov_len = cp - data->sp; + data->sp = cp; + + if (iov_index == iov_alloc) + /* This should not ordinarily happen. */ + { + iov_alloc *= 2; + if (iov != small_iov) { + zlog_warn("%s: growing iov array to %d; " + "width %d, height %d, size %lu", + __func__, iov_alloc, width, height, + (u_long) b->size); + iov = + XREALLOC(MTYPE_TMP, iov, + iov_alloc * sizeof(*iov)); + } else { + /* This should absolutely never occur. */ + zlog_err + ("%s: corruption detected: iov_small overflowed; " + "head %p, tail %p, head->next %p", + __func__, b->head, b->tail, b->head->next); + iov = + XMALLOC(MTYPE_TMP, + iov_alloc * sizeof(*iov)); + memcpy(iov, small_iov, sizeof(small_iov)); + } + } + } + + /* In case of `more' display need. */ + if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) { + iov[iov_index].iov_base = more; + iov[iov_index].iov_len = sizeof more; + iov_index++; + } +#ifdef IOV_MAX + /* IOV_MAX are normally defined in , Posix.1g. + example: Solaris2.6 are defined IOV_MAX size at 16. */ + { + struct iovec *c_iov = iov; + nbytes = 0; /* Make sure it's initialized. */ + + while (iov_index > 0) { + int iov_size; + + iov_size = + ((iov_index > IOV_MAX) ? IOV_MAX : iov_index); + if ((nbytes = writev(fd, c_iov, iov_size)) < 0) { + zlog_warn("%s: writev to fd %d failed: %s", + __func__, fd, safe_strerror(errno)); + break; + } + + /* move pointer io-vector */ + c_iov += iov_size; + iov_index -= iov_size; + } + } +#else /* IOV_MAX */ + if ((nbytes = writev(fd, iov, iov_index)) < 0) + zlog_warn("%s: writev to fd %d failed: %s", + __func__, fd, safe_strerror(errno)); +#endif /* IOV_MAX */ + + /* Free printed buffer data. */ + while (b->head && (b->head->sp == b->head->cp)) { + struct buffer_data *del; + if (!(b->head = (del = b->head)->next)) + b->tail = NULL; + BUFFER_DATA_FREE(del); + } + + if (iov != small_iov) + XFREE(MTYPE_TMP, iov); + + return (nbytes < 0) ? BUFFER_ERROR : + (b->head ? BUFFER_PENDING : BUFFER_EMPTY); +} +#endif + +/* This function (unlike other buffer_flush* functions above) is designed +to work with non-blocking sockets. It does not attempt to write out +all of the queued data, just a "big" chunk. It returns 0 if it was +able to empty out the buffers completely, 1 if more flushing is +required later, or -1 on a fatal write error. */ +buffer_status_t buffer_flush_available(struct buffer * b, int fd) +{ + +/* These are just reasonable values to make sure a significant amount of +data is written. There's no need to go crazy and try to write it all +in one shot. */ +#ifdef IOV_MAX +#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX) +#else +#define MAX_CHUNKS 16 +#endif +#define MAX_FLUSH 131072 + + struct buffer_data *d; + size_t written; + struct iovec iov[MAX_CHUNKS]; + size_t iovcnt = 0; + size_t nbyte = 0; + + for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH); + d = d->next, iovcnt++) { + iov[iovcnt].iov_base = d->data + d->sp; + nbyte += (iov[iovcnt].iov_len = d->cp - d->sp); + } + + if (!nbyte) + /* No data to flush: should we issue a warning message? */ + return BUFFER_EMPTY; + + /* only place where written should be sign compared */ + if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) { + if (ERRNO_IO_RETRY(errno)) + /* Calling code should try again later. */ + return BUFFER_PENDING; + return BUFFER_ERROR; + } + + /* Free printed buffer data. */ + while (written > 0) { + struct buffer_data *d; + if (!(d = b->head)) + break; + if (written < d->cp - d->sp) { + d->sp += written; + return BUFFER_PENDING; + } + + written -= (d->cp - d->sp); + if (!(b->head = d->next)) + b->tail = NULL; + BUFFER_DATA_FREE(d); + } + + return b->head ? BUFFER_PENDING : BUFFER_EMPTY; + +#undef MAX_CHUNKS +#undef MAX_FLUSH +} + +buffer_status_t +buffer_write(struct buffer * b, int fd, const void *p, size_t size) +{ + ssize_t nbytes; + +#if 0 + /* Should we attempt to drain any previously buffered data? This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */ + + if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR)) + return BUFFER_ERROR; +#endif + if (b->head) + /* Buffer is not empty, so do not attempt to write the new data. */ + nbytes = 0; + else if ((nbytes = write(fd, p, size)) < 0) { + if (ERRNO_IO_RETRY(errno)) + nbytes = 0; + else + return BUFFER_ERROR; + } + /* Add any remaining data to the buffer. */ + { + size_t written = nbytes; + if (written < size) + buffer_put(b, ((const char *)p) + written, + size - written); + } + return b->head ? BUFFER_PENDING : BUFFER_EMPTY; +} diff --git a/src/vty/command.c b/src/vty/command.c new file mode 100644 index 00000000..7275a3d4 --- /dev/null +++ b/src/vty/command.c @@ -0,0 +1,3176 @@ +/* + $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $ + + Command interpreter routine for virtual terminal [aka TeletYpe] + Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + +This file is part of GNU Zebra. + +GNU Zebra 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 2, or (at your +option) any later version. + +GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include +#include +#include +#include +#include +#define _XOPEN_SOURCE +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define CONFIGFILE_MASK 022 + +void *tall_vty_cmd_ctx; + +/* Command vector which includes some level of command lists. Normally + each daemon maintains each own cmdvec. */ +vector cmdvec; + +/* Host information structure. */ +struct host host; + +/* Standard command node structures. */ +struct cmd_node auth_node = { + AUTH_NODE, + "Password: ", +}; + +struct cmd_node view_node = { + VIEW_NODE, + "%s> ", +}; + +struct cmd_node auth_enable_node = { + AUTH_ENABLE_NODE, + "Password: ", +}; + +struct cmd_node enable_node = { + ENABLE_NODE, + "%s# ", +}; + +struct cmd_node config_node = { + CONFIG_NODE, + "%s(config)# ", + 1 +}; + +/* Default motd string. */ +const char *default_motd = ""; + +/* This is called from main when a daemon is invoked with -v or --version. */ +void print_version(int print_copyright) +{ + printf("%s version %s\n", host.prog_name, host.prog_version); + if (print_copyright) + printf("\n%s\n", host.prog_copyright); +} + +/* Utility function to concatenate argv argument into a single string + with inserting ' ' character between each argument. */ +char *argv_concat(const char **argv, int argc, int shift) +{ + int i; + size_t len; + char *str; + char *p; + + len = 0; + for (i = shift; i < argc; i++) + len += strlen(argv[i]) + 1; + if (!len) + return NULL; + p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat"); + for (i = shift; i < argc; i++) { + size_t arglen; + memcpy(p, argv[i], (arglen = strlen(argv[i]))); + p += arglen; + *p++ = ' '; + } + *(p - 1) = '\0'; + return str; +} + +/* Install top node of command vector. */ +void install_node(struct cmd_node *node, int (*func) (struct vty *)) +{ + vector_set_index(cmdvec, node->node, node); + node->func = func; + node->cmd_vector = vector_init(VECTOR_MIN_SIZE); +} + +/* Compare two command's string. Used in sort_node (). */ +static int cmp_node(const void *p, const void *q) +{ + struct cmd_element *a = *(struct cmd_element **)p; + struct cmd_element *b = *(struct cmd_element **)q; + + return strcmp(a->string, b->string); +} + +static int cmp_desc(const void *p, const void *q) +{ + struct desc *a = *(struct desc **)p; + struct desc *b = *(struct desc **)q; + + return strcmp(a->cmd, b->cmd); +} + +/* Sort each node's command element according to command string. */ +void sort_node() +{ + unsigned int i, j; + struct cmd_node *cnode; + vector descvec; + struct cmd_element *cmd_element; + + for (i = 0; i < vector_active(cmdvec); i++) + if ((cnode = vector_slot(cmdvec, i)) != NULL) { + vector cmd_vector = cnode->cmd_vector; + qsort(cmd_vector->index, vector_active(cmd_vector), + sizeof(void *), cmp_node); + + for (j = 0; j < vector_active(cmd_vector); j++) + if ((cmd_element = + vector_slot(cmd_vector, j)) != NULL + && vector_active(cmd_element->strvec)) { + descvec = + vector_slot(cmd_element->strvec, + vector_active + (cmd_element->strvec) - + 1); + qsort(descvec->index, + vector_active(descvec), + sizeof(void *), cmp_desc); + } + } +} + +/* Breaking up string into each command piece. I assume given + character is separated by a space character. Return value is a + vector which includes char ** data element. */ +vector cmd_make_strvec(const char *string) +{ + const char *cp, *start; + char *token; + int strlen; + vector strvec; + + if (string == NULL) + return NULL; + + cp = string; + + /* Skip white spaces. */ + while (isspace((int)*cp) && *cp != '\0') + cp++; + + /* Return if there is only white spaces */ + if (*cp == '\0') + return NULL; + + if (*cp == '!' || *cp == '#') + return NULL; + + /* Prepare return vector. */ + strvec = vector_init(VECTOR_MIN_SIZE); + + /* Copy each command piece and set into vector. */ + while (1) { + start = cp; + while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') && + *cp != '\0') + cp++; + strlen = cp - start; + token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec"); + memcpy(token, start, strlen); + *(token + strlen) = '\0'; + vector_set(strvec, token); + + while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') && + *cp != '\0') + cp++; + + if (*cp == '\0') + return strvec; + } +} + +/* Free allocated string vector. */ +void cmd_free_strvec(vector v) +{ + unsigned int i; + char *cp; + + if (!v) + return; + + for (i = 0; i < vector_active(v); i++) + if ((cp = vector_slot(v, i)) != NULL) + talloc_free(cp); + + vector_free(v); +} + +/* Fetch next description. Used in cmd_make_descvec(). */ +static char *cmd_desc_str(const char **string) +{ + const char *cp, *start; + char *token; + int strlen; + + cp = *string; + + if (cp == NULL) + return NULL; + + /* Skip white spaces. */ + while (isspace((int)*cp) && *cp != '\0') + cp++; + + /* Return if there is only white spaces */ + if (*cp == '\0') + return NULL; + + start = cp; + + while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') + cp++; + + strlen = cp - start; + token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str"); + memcpy(token, start, strlen); + *(token + strlen) = '\0'; + + *string = cp; + + return token; +} + +/* New string vector. */ +static vector cmd_make_descvec(const char *string, const char *descstr) +{ + int multiple = 0; + const char *sp; + char *token; + int len; + const char *cp; + const char *dp; + vector allvec; + vector strvec = NULL; + struct desc *desc; + + cp = string; + dp = descstr; + + if (cp == NULL) + return NULL; + + allvec = vector_init(VECTOR_MIN_SIZE); + + while (1) { + while (isspace((int)*cp) && *cp != '\0') + cp++; + + if (*cp == '(') { + multiple = 1; + cp++; + } + if (*cp == ')') { + multiple = 0; + cp++; + } + if (*cp == '|') { + if (!multiple) { + fprintf(stderr, "Command parse error!: %s\n", + string); + exit(1); + } + cp++; + } + + while (isspace((int)*cp) && *cp != '\0') + cp++; + + if (*cp == '(') { + multiple = 1; + cp++; + } + + if (*cp == '\0') + return allvec; + + sp = cp; + + while (! + (isspace((int)*cp) || *cp == '\r' || *cp == '\n' + || *cp == ')' || *cp == '|') && *cp != '\0') + cp++; + + len = cp - sp; + + token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec"); + memcpy(token, sp, len); + *(token + len) = '\0'; + + desc = talloc_zero(tall_vty_cmd_ctx, struct desc); + desc->cmd = token; + desc->str = cmd_desc_str(&dp); + + if (multiple) { + if (multiple == 1) { + strvec = vector_init(VECTOR_MIN_SIZE); + vector_set(allvec, strvec); + } + multiple++; + } else { + strvec = vector_init(VECTOR_MIN_SIZE); + vector_set(allvec, strvec); + } + vector_set(strvec, desc); + } +} + +/* Count mandantory string vector size. This is to determine inputed + command has enough command length. */ +static int cmd_cmdsize(vector strvec) +{ + unsigned int i; + int size = 0; + vector descvec; + struct desc *desc; + + for (i = 0; i < vector_active(strvec); i++) + if ((descvec = vector_slot(strvec, i)) != NULL) { + if ((vector_active(descvec)) == 1 + && (desc = vector_slot(descvec, 0)) != NULL) { + if (desc->cmd == NULL || CMD_OPTION(desc->cmd)) + return size; + else + size++; + } else + size++; + } + return size; +} + +/* Return prompt character of specified node. */ +const char *cmd_prompt(enum node_type node) +{ + struct cmd_node *cnode; + + cnode = vector_slot(cmdvec, node); + return cnode->prompt; +} + +/* Install a command into a node. */ +void install_element(enum node_type ntype, struct cmd_element *cmd) +{ + struct cmd_node *cnode; + + cnode = vector_slot(cmdvec, ntype); + + if (cnode == NULL) { + fprintf(stderr, + "Command node %d doesn't exist, please check it\n", + ntype); + exit(1); + } + + vector_set(cnode->cmd_vector, cmd); + + cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc); + cmd->cmdsize = cmd_cmdsize(cmd->strvec); +} + +/* Install a command into VIEW and ENABLE node */ +void install_element_ve(struct cmd_element *cmd) +{ + install_element(VIEW_NODE, cmd); + install_element(ENABLE_NODE, cmd); +} + +#ifdef VTY_CRYPT_PW +static unsigned char itoa64[] = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void to64(char *s, long v, int n) +{ + while (--n >= 0) { + *s++ = itoa64[v & 0x3f]; + v >>= 6; + } +} + +static char *zencrypt(const char *passwd) +{ + char salt[6]; + struct timeval tv; + char *crypt(const char *, const char *); + + gettimeofday(&tv, 0); + + to64(&salt[0], random(), 3); + to64(&salt[3], tv.tv_usec, 3); + salt[5] = '\0'; + + return crypt(passwd, salt); +} +#endif + +/* This function write configuration of this host. */ +static int config_write_host(struct vty *vty) +{ + if (host.name) + vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE); + + if (host.encrypt) { + if (host.password_encrypt) + vty_out(vty, "password 8 %s%s", host.password_encrypt, + VTY_NEWLINE); + if (host.enable_encrypt) + vty_out(vty, "enable password 8 %s%s", + host.enable_encrypt, VTY_NEWLINE); + } else { + if (host.password) + vty_out(vty, "password %s%s", host.password, + VTY_NEWLINE); + if (host.enable) + vty_out(vty, "enable password %s%s", host.enable, + VTY_NEWLINE); + } + + if (host.advanced) + vty_out(vty, "service advanced-vty%s", VTY_NEWLINE); + + if (host.encrypt) + vty_out(vty, "service password-encryption%s", VTY_NEWLINE); + + if (host.lines >= 0) + vty_out(vty, "service terminal-length %d%s", host.lines, + VTY_NEWLINE); + + if (host.motdfile) + vty_out(vty, "banner motd file %s%s", host.motdfile, + VTY_NEWLINE); + else if (!host.motd) + vty_out(vty, "no banner motd%s", VTY_NEWLINE); + + return 1; +} + +/* Utility function for getting command vector. */ +static vector cmd_node_vector(vector v, enum node_type ntype) +{ + struct cmd_node *cnode = vector_slot(v, ntype); + return cnode->cmd_vector; +} + +/* Completion match types. */ +enum match_type { + no_match, + extend_match, + ipv4_prefix_match, + ipv4_match, + ipv6_prefix_match, + ipv6_match, + range_match, + vararg_match, + partly_match, + exact_match +}; + +static enum match_type cmd_ipv4_match(const char *str) +{ + const char *sp; + int dots = 0, nums = 0; + char buf[4]; + + if (str == NULL) + return partly_match; + + for (;;) { + memset(buf, 0, sizeof(buf)); + sp = str; + while (*str != '\0') { + if (*str == '.') { + if (dots >= 3) + return no_match; + + if (*(str + 1) == '.') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + if (!isdigit((int)*str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy(buf, sp, str - sp); + if (atoi(buf) > 255) + return no_match; + + nums++; + + if (*str == '\0') + break; + + str++; + } + + if (nums < 4) + return partly_match; + + return exact_match; +} + +static enum match_type cmd_ipv4_prefix_match(const char *str) +{ + const char *sp; + int dots = 0; + char buf[4]; + + if (str == NULL) + return partly_match; + + for (;;) { + memset(buf, 0, sizeof(buf)); + sp = str; + while (*str != '\0' && *str != '/') { + if (*str == '.') { + if (dots == 3) + return no_match; + + if (*(str + 1) == '.' || *(str + 1) == '/') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + + if (!isdigit((int)*str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy(buf, sp, str - sp); + if (atoi(buf) > 255) + return no_match; + + if (dots == 3) { + if (*str == '/') { + if (*(str + 1) == '\0') + return partly_match; + + str++; + break; + } else if (*str == '\0') + return partly_match; + } + + if (*str == '\0') + return partly_match; + + str++; + } + + sp = str; + while (*str != '\0') { + if (!isdigit((int)*str)) + return no_match; + + str++; + } + + if (atoi(sp) > 32) + return no_match; + + return exact_match; +} + +#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%" +#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/" +#define STATE_START 1 +#define STATE_COLON 2 +#define STATE_DOUBLE 3 +#define STATE_ADDR 4 +#define STATE_DOT 5 +#define STATE_SLASH 6 +#define STATE_MASK 7 + +#ifdef HAVE_IPV6 + +static enum match_type cmd_ipv6_match(const char *str) +{ + int state = STATE_START; + int colons = 0, nums = 0, double_colon = 0; + const char *sp = NULL; + struct sockaddr_in6 sin6_dummy; + int ret; + + if (str == NULL) + return partly_match; + + if (strspn(str, IPV6_ADDR_STR) != strlen(str)) + return no_match; + + /* use inet_pton that has a better support, + * for example inet_pton can support the automatic addresses: + * ::1.2.3.4 + */ + ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); + + if (ret == 1) + return exact_match; + + while (*str != '\0') { + switch (state) { + case STATE_START: + if (*str == ':') { + if (*(str + 1) != ':' && *(str + 1) != '\0') + return no_match; + colons--; + state = STATE_COLON; + } else { + sp = str; + state = STATE_ADDR; + } + + continue; + case STATE_COLON: + colons++; + if (*(str + 1) == ':') + state = STATE_DOUBLE; + else { + sp = str + 1; + state = STATE_ADDR; + } + break; + case STATE_DOUBLE: + if (double_colon) + return no_match; + + if (*(str + 1) == ':') + return no_match; + else { + if (*(str + 1) != '\0') + colons++; + sp = str + 1; + state = STATE_ADDR; + } + + double_colon++; + nums++; + break; + case STATE_ADDR: + if (*(str + 1) == ':' || *(str + 1) == '\0') { + if (str - sp > 3) + return no_match; + + nums++; + state = STATE_COLON; + } + if (*(str + 1) == '.') + state = STATE_DOT; + break; + case STATE_DOT: + state = STATE_ADDR; + break; + default: + break; + } + + if (nums > 8) + return no_match; + + if (colons > 7) + return no_match; + + str++; + } + +#if 0 + if (nums < 11) + return partly_match; +#endif /* 0 */ + + return exact_match; +} + +static enum match_type cmd_ipv6_prefix_match(const char *str) +{ + int state = STATE_START; + int colons = 0, nums = 0, double_colon = 0; + int mask; + const char *sp = NULL; + char *endptr = NULL; + + if (str == NULL) + return partly_match; + + if (strspn(str, IPV6_PREFIX_STR) != strlen(str)) + return no_match; + + while (*str != '\0' && state != STATE_MASK) { + switch (state) { + case STATE_START: + if (*str == ':') { + if (*(str + 1) != ':' && *(str + 1) != '\0') + return no_match; + colons--; + state = STATE_COLON; + } else { + sp = str; + state = STATE_ADDR; + } + + continue; + case STATE_COLON: + colons++; + if (*(str + 1) == '/') + return no_match; + else if (*(str + 1) == ':') + state = STATE_DOUBLE; + else { + sp = str + 1; + state = STATE_ADDR; + } + break; + case STATE_DOUBLE: + if (double_colon) + return no_match; + + if (*(str + 1) == ':') + return no_match; + else { + if (*(str + 1) != '\0' && *(str + 1) != '/') + colons++; + sp = str + 1; + + if (*(str + 1) == '/') + state = STATE_SLASH; + else + state = STATE_ADDR; + } + + double_colon++; + nums += 1; + break; + case STATE_ADDR: + if (*(str + 1) == ':' || *(str + 1) == '.' + || *(str + 1) == '\0' || *(str + 1) == '/') { + if (str - sp > 3) + return no_match; + + for (; sp <= str; sp++) + if (*sp == '/') + return no_match; + + nums++; + + if (*(str + 1) == ':') + state = STATE_COLON; + else if (*(str + 1) == '.') + state = STATE_DOT; + else if (*(str + 1) == '/') + state = STATE_SLASH; + } + break; + case STATE_DOT: + state = STATE_ADDR; + break; + case STATE_SLASH: + if (*(str + 1) == '\0') + return partly_match; + + state = STATE_MASK; + break; + default: + break; + } + + if (nums > 11) + return no_match; + + if (colons > 7) + return no_match; + + str++; + } + + if (state < STATE_MASK) + return partly_match; + + mask = strtol(str, &endptr, 10); + if (*endptr != '\0') + return no_match; + + if (mask < 0 || mask > 128) + return no_match; + +/* I don't know why mask < 13 makes command match partly. + Forgive me to make this comments. I Want to set static default route + because of lack of function to originate default in ospf6d; sorry + yasu + if (mask < 13) + return partly_match; +*/ + + return exact_match; +} + +#endif /* HAVE_IPV6 */ + +#define DECIMAL_STRLEN_MAX 10 + +static int cmd_range_match(const char *range, const char *str) +{ + char *p; + char buf[DECIMAL_STRLEN_MAX + 1]; + char *endptr = NULL; + unsigned long min, max, val; + + if (str == NULL) + return 1; + + val = strtoul(str, &endptr, 10); + if (*endptr != '\0') + return 0; + + range++; + p = strchr(range, '-'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + strncpy(buf, range, p - range); + buf[p - range] = '\0'; + min = strtoul(buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + range = p + 1; + p = strchr(range, '>'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + strncpy(buf, range, p - range); + buf[p - range] = '\0'; + max = strtoul(buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + if (val < min || val > max) + return 0; + + return 1; +} + +/* Make completion match and return match type flag. */ +static enum match_type +cmd_filter_by_completion(char *command, vector v, unsigned int index) +{ + unsigned int i; + const char *str; + struct cmd_element *cmd_element; + enum match_type match_type; + vector descvec; + struct desc *desc; + + match_type = no_match; + + /* If command and cmd_element string does not match set NULL to vector */ + for (i = 0; i < vector_active(v); i++) + if ((cmd_element = vector_slot(v, i)) != NULL) { + if (index >= vector_active(cmd_element->strvec)) + vector_slot(v, i) = NULL; + else { + unsigned int j; + int matched = 0; + + descvec = + vector_slot(cmd_element->strvec, index); + + for (j = 0; j < vector_active(descvec); j++) + if ((desc = vector_slot(descvec, j))) { + str = desc->cmd; + + if (CMD_VARARG(str)) { + if (match_type < + vararg_match) + match_type = + vararg_match; + matched++; + } else if (CMD_RANGE(str)) { + if (cmd_range_match + (str, command)) { + if (match_type < + range_match) + match_type + = + range_match; + + matched++; + } + } +#ifdef HAVE_IPV6 + else if (CMD_IPV6(str)) { + if (cmd_ipv6_match + (command)) { + if (match_type < + ipv6_match) + match_type + = + ipv6_match; + + matched++; + } + } else if (CMD_IPV6_PREFIX(str)) { + if (cmd_ipv6_prefix_match(command)) { + if (match_type < + ipv6_prefix_match) + match_type + = + ipv6_prefix_match; + + matched++; + } + } +#endif /* HAVE_IPV6 */ + else if (CMD_IPV4(str)) { + if (cmd_ipv4_match + (command)) { + if (match_type < + ipv4_match) + match_type + = + ipv4_match; + + matched++; + } + } else if (CMD_IPV4_PREFIX(str)) { + if (cmd_ipv4_prefix_match(command)) { + if (match_type < + ipv4_prefix_match) + match_type + = + ipv4_prefix_match; + matched++; + } + } else + /* Check is this point's argument optional ? */ + if (CMD_OPTION(str) + || + CMD_VARIABLE(str)) { + if (match_type < + extend_match) + match_type = + extend_match; + matched++; + } else + if (strncmp + (command, str, + strlen(command)) == + 0) { + if (strcmp(command, str) + == 0) + match_type = + exact_match; + else { + if (match_type < + partly_match) + match_type + = + partly_match; + } + matched++; + } + } + if (!matched) + vector_slot(v, i) = NULL; + } + } + return match_type; +} + +/* Filter vector by command character with index. */ +static enum match_type +cmd_filter_by_string(char *command, vector v, unsigned int index) +{ + unsigned int i; + const char *str; + struct cmd_element *cmd_element; + enum match_type match_type; + vector descvec; + struct desc *desc; + + match_type = no_match; + + /* If command and cmd_element string does not match set NULL to vector */ + for (i = 0; i < vector_active(v); i++) + if ((cmd_element = vector_slot(v, i)) != NULL) { + /* If given index is bigger than max string vector of command, + set NULL */ + if (index >= vector_active(cmd_element->strvec)) + vector_slot(v, i) = NULL; + else { + unsigned int j; + int matched = 0; + + descvec = + vector_slot(cmd_element->strvec, index); + + for (j = 0; j < vector_active(descvec); j++) + if ((desc = vector_slot(descvec, j))) { + str = desc->cmd; + + if (CMD_VARARG(str)) { + if (match_type < + vararg_match) + match_type = + vararg_match; + matched++; + } else if (CMD_RANGE(str)) { + if (cmd_range_match + (str, command)) { + if (match_type < + range_match) + match_type + = + range_match; + matched++; + } + } +#ifdef HAVE_IPV6 + else if (CMD_IPV6(str)) { + if (cmd_ipv6_match + (command) == + exact_match) { + if (match_type < + ipv6_match) + match_type + = + ipv6_match; + matched++; + } + } else if (CMD_IPV6_PREFIX(str)) { + if (cmd_ipv6_prefix_match(command) == exact_match) { + if (match_type < + ipv6_prefix_match) + match_type + = + ipv6_prefix_match; + matched++; + } + } +#endif /* HAVE_IPV6 */ + else if (CMD_IPV4(str)) { + if (cmd_ipv4_match + (command) == + exact_match) { + if (match_type < + ipv4_match) + match_type + = + ipv4_match; + matched++; + } + } else if (CMD_IPV4_PREFIX(str)) { + if (cmd_ipv4_prefix_match(command) == exact_match) { + if (match_type < + ipv4_prefix_match) + match_type + = + ipv4_prefix_match; + matched++; + } + } else if (CMD_OPTION(str) + || CMD_VARIABLE(str)) + { + if (match_type < + extend_match) + match_type = + extend_match; + matched++; + } else { + if (strcmp(command, str) + == 0) { + match_type = + exact_match; + matched++; + } + } + } + if (!matched) + vector_slot(v, i) = NULL; + } + } + return match_type; +} + +/* Check ambiguous match */ +static int +is_cmd_ambiguous(char *command, vector v, int index, enum match_type type) +{ + unsigned int i; + unsigned int j; + const char *str = NULL; + struct cmd_element *cmd_element; + const char *matched = NULL; + vector descvec; + struct desc *desc; + + for (i = 0; i < vector_active(v); i++) + if ((cmd_element = vector_slot(v, i)) != NULL) { + int match = 0; + + descvec = vector_slot(cmd_element->strvec, index); + + for (j = 0; j < vector_active(descvec); j++) + if ((desc = vector_slot(descvec, j))) { + enum match_type ret; + + str = desc->cmd; + + switch (type) { + case exact_match: + if (! + (CMD_OPTION(str) + || CMD_VARIABLE(str)) +&& strcmp(command, str) == 0) + match++; + break; + case partly_match: + if (! + (CMD_OPTION(str) + || CMD_VARIABLE(str)) +&& strncmp(command, str, strlen(command)) == 0) { + if (matched + && strcmp(matched, + str) != 0) + return 1; /* There is ambiguous match. */ + else + matched = str; + match++; + } + break; + case range_match: + if (cmd_range_match + (str, command)) { + if (matched + && strcmp(matched, + str) != 0) + return 1; + else + matched = str; + match++; + } + break; +#ifdef HAVE_IPV6 + case ipv6_match: + if (CMD_IPV6(str)) + match++; + break; + case ipv6_prefix_match: + if ((ret = + cmd_ipv6_prefix_match + (command)) != no_match) { + if (ret == partly_match) + return 2; /* There is incomplete match. */ + + match++; + } + break; +#endif /* HAVE_IPV6 */ + case ipv4_match: + if (CMD_IPV4(str)) + match++; + break; + case ipv4_prefix_match: + if ((ret = + cmd_ipv4_prefix_match + (command)) != no_match) { + if (ret == partly_match) + return 2; /* There is incomplete match. */ + + match++; + } + break; + case extend_match: + if (CMD_OPTION(str) + || CMD_VARIABLE(str)) + match++; + break; + case no_match: + default: + break; + } + } + if (!match) + vector_slot(v, i) = NULL; + } + return 0; +} + +/* If src matches dst return dst string, otherwise return NULL */ +static const char *cmd_entry_function(const char *src, const char *dst) +{ + /* Skip variable arguments. */ + if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) || + CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst)) + return NULL; + + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + return dst; + + /* Matched with input string. */ + if (strncmp(src, dst, strlen(src)) == 0) + return dst; + + return NULL; +} + +/* If src matches dst return dst string, otherwise return NULL */ +/* This version will return the dst string always if it is + CMD_VARIABLE for '?' key processing */ +static const char *cmd_entry_function_desc(const char *src, const char *dst) +{ + if (CMD_VARARG(dst)) + return dst; + + if (CMD_RANGE(dst)) { + if (cmd_range_match(dst, src)) + return dst; + else + return NULL; + } +#ifdef HAVE_IPV6 + if (CMD_IPV6(dst)) { + if (cmd_ipv6_match(src)) + return dst; + else + return NULL; + } + + if (CMD_IPV6_PREFIX(dst)) { + if (cmd_ipv6_prefix_match(src)) + return dst; + else + return NULL; + } +#endif /* HAVE_IPV6 */ + + if (CMD_IPV4(dst)) { + if (cmd_ipv4_match(src)) + return dst; + else + return NULL; + } + + if (CMD_IPV4_PREFIX(dst)) { + if (cmd_ipv4_prefix_match(src)) + return dst; + else + return NULL; + } + + /* Optional or variable commands always match on '?' */ + if (CMD_OPTION(dst) || CMD_VARIABLE(dst)) + return dst; + + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + return dst; + + if (strncmp(src, dst, strlen(src)) == 0) + return dst; + else + return NULL; +} + +/* Check same string element existence. If it isn't there return + 1. */ +static int cmd_unique_string(vector v, const char *str) +{ + unsigned int i; + char *match; + + for (i = 0; i < vector_active(v); i++) + if ((match = vector_slot(v, i)) != NULL) + if (strcmp(match, str) == 0) + return 0; + return 1; +} + +/* Compare string to description vector. If there is same string + return 1 else return 0. */ +static int desc_unique_string(vector v, const char *str) +{ + unsigned int i; + struct desc *desc; + + for (i = 0; i < vector_active(v); i++) + if ((desc = vector_slot(v, i)) != NULL) + if (strcmp(desc->cmd, str) == 0) + return 1; + return 0; +} + +static int cmd_try_do_shortcut(enum node_type node, char *first_word) +{ + if (first_word != NULL && + node != AUTH_NODE && + node != VIEW_NODE && + node != AUTH_ENABLE_NODE && + node != ENABLE_NODE && 0 == strcmp("do", first_word)) + return 1; + return 0; +} + +/* '?' describe command support. */ +static vector +cmd_describe_command_real(vector vline, struct vty *vty, int *status) +{ + unsigned int i; + vector cmd_vector; +#define INIT_MATCHVEC_SIZE 10 + vector matchvec; + struct cmd_element *cmd_element; + unsigned int index; + int ret; + enum match_type match; + char *command; + static struct desc desc_cr = { "", "" }; + + /* Set index. */ + if (vector_active(vline) == 0) { + *status = CMD_ERR_NO_MATCH; + return NULL; + } else + index = vector_active(vline) - 1; + + /* Make copy vector of current node's command vector. */ + cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); + + /* Prepare match vector */ + matchvec = vector_init(INIT_MATCHVEC_SIZE); + + /* Filter commands. */ + /* Only words precedes current word will be checked in this loop. */ + for (i = 0; i < index; i++) + if ((command = vector_slot(vline, i))) { + match = + cmd_filter_by_completion(command, cmd_vector, i); + + if (match == vararg_match) { + struct cmd_element *cmd_element; + vector descvec; + unsigned int j, k; + + for (j = 0; j < vector_active(cmd_vector); j++) + if ((cmd_element = + vector_slot(cmd_vector, j)) != NULL + && + (vector_active + (cmd_element->strvec))) { + descvec = + vector_slot(cmd_element-> + strvec, + vector_active + (cmd_element-> + strvec) - 1); + for (k = 0; + k < vector_active(descvec); + k++) { + struct desc *desc = + vector_slot(descvec, + k); + vector_set(matchvec, + desc); + } + } + + vector_set(matchvec, &desc_cr); + vector_free(cmd_vector); + + return matchvec; + } + + if ((ret = + is_cmd_ambiguous(command, cmd_vector, i, + match)) == 1) { + vector_free(cmd_vector); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } else if (ret == 2) { + vector_free(cmd_vector); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + } + + /* Prepare match vector */ + /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ + + /* Make sure that cmd_vector is filtered based on current word */ + command = vector_slot(vline, index); + if (command) + match = cmd_filter_by_completion(command, cmd_vector, index); + + /* Make description vector. */ + for (i = 0; i < vector_active(cmd_vector); i++) + if ((cmd_element = vector_slot(cmd_vector, i)) != NULL) { + const char *string = NULL; + vector strvec = cmd_element->strvec; + + /* if command is NULL, index may be equal to vector_active */ + if (command && index >= vector_active(strvec)) + vector_slot(cmd_vector, i) = NULL; + else { + /* Check if command is completed. */ + if (command == NULL + && index == vector_active(strvec)) { + string = ""; + if (!desc_unique_string + (matchvec, string)) + vector_set(matchvec, &desc_cr); + } else { + unsigned int j; + vector descvec = + vector_slot(strvec, index); + struct desc *desc; + + for (j = 0; j < vector_active(descvec); + j++) + if ((desc = + vector_slot(descvec, j))) { + string = + cmd_entry_function_desc + (command, + desc->cmd); + if (string) { + /* Uniqueness check */ + if (!desc_unique_string(matchvec, string)) + vector_set + (matchvec, + desc); + } + } + } + } + } + vector_free(cmd_vector); + + if (vector_slot(matchvec, 0) == NULL) { + vector_free(matchvec); + *status = CMD_ERR_NO_MATCH; + } else + *status = CMD_SUCCESS; + + return matchvec; +} + +vector cmd_describe_command(vector vline, struct vty * vty, int *status) +{ + vector ret; + + if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { + enum node_type onode; + vector shifted_vline; + unsigned int index; + + onode = vty->node; + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ + + shifted_vline = vector_init(vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active(vline); index++) { + vector_set_index(shifted_vline, index - 1, + vector_lookup(vline, index)); + } + + ret = cmd_describe_command_real(shifted_vline, vty, status); + + vector_free(shifted_vline); + vty->node = onode; + return ret; + } + + return cmd_describe_command_real(vline, vty, status); +} + +/* Check LCD of matched command. */ +static int cmd_lcd(char **matched) +{ + int i; + int j; + int lcd = -1; + char *s1, *s2; + char c1, c2; + + if (matched[0] == NULL || matched[1] == NULL) + return 0; + + for (i = 1; matched[i] != NULL; i++) { + s1 = matched[i - 1]; + s2 = matched[i]; + + for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++) + if (c1 != c2) + break; + + if (lcd < 0) + lcd = j; + else { + if (lcd > j) + lcd = j; + } + } + return lcd; +} + +/* Command line completion support. */ +static char **cmd_complete_command_real(vector vline, struct vty *vty, + int *status) +{ + unsigned int i; + vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); +#define INIT_MATCHVEC_SIZE 10 + vector matchvec; + struct cmd_element *cmd_element; + unsigned int index; + char **match_str; + struct desc *desc; + vector descvec; + char *command; + int lcd; + + if (vector_active(vline) == 0) { + *status = CMD_ERR_NO_MATCH; + return NULL; + } else + index = vector_active(vline) - 1; + + /* First, filter by preceeding command string */ + for (i = 0; i < index; i++) + if ((command = vector_slot(vline, i))) { + enum match_type match; + int ret; + + /* First try completion match, if there is exactly match return 1 */ + match = + cmd_filter_by_completion(command, cmd_vector, i); + + /* If there is exact match then filter ambiguous match else check + ambiguousness. */ + if ((ret = + is_cmd_ambiguous(command, cmd_vector, i, + match)) == 1) { + vector_free(cmd_vector); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } + /* + else if (ret == 2) + { + vector_free (cmd_vector); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + */ + } + + /* Prepare match vector. */ + matchvec = vector_init(INIT_MATCHVEC_SIZE); + + /* Now we got into completion */ + for (i = 0; i < vector_active(cmd_vector); i++) + if ((cmd_element = vector_slot(cmd_vector, i))) { + const char *string; + vector strvec = cmd_element->strvec; + + /* Check field length */ + if (index >= vector_active(strvec)) + vector_slot(cmd_vector, i) = NULL; + else { + unsigned int j; + + descvec = vector_slot(strvec, index); + for (j = 0; j < vector_active(descvec); j++) + if ((desc = vector_slot(descvec, j))) { + if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd))) + if (cmd_unique_string (matchvec, string)) + vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string)); + } + } + } + + /* We don't need cmd_vector any more. */ + vector_free(cmd_vector); + + /* No matched command */ + if (vector_slot(matchvec, 0) == NULL) { + vector_free(matchvec); + + /* In case of 'command \t' pattern. Do you need '?' command at + the end of the line. */ + if (vector_slot(vline, index) == '\0') + *status = CMD_ERR_NOTHING_TODO; + else + *status = CMD_ERR_NO_MATCH; + return NULL; + } + + /* Only one matched */ + if (vector_slot(matchvec, 1) == NULL) { + match_str = (char **)matchvec->index; + vector_only_wrapper_free(matchvec); + *status = CMD_COMPLETE_FULL_MATCH; + return match_str; + } + /* Make it sure last element is NULL. */ + vector_set(matchvec, NULL); + + /* Check LCD of matched strings. */ + if (vector_slot(vline, index) != NULL) { + lcd = cmd_lcd((char **)matchvec->index); + + if (lcd) { + int len = strlen(vector_slot(vline, index)); + + if (len < lcd) { + char *lcdstr; + + lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1, + "complete-lcdstr"); + memcpy(lcdstr, matchvec->index[0], lcd); + lcdstr[lcd] = '\0'; + + /* match_str = (char **) &lcdstr; */ + + /* Free matchvec. */ + for (i = 0; i < vector_active(matchvec); i++) { + if (vector_slot(matchvec, i)) + talloc_free(vector_slot(matchvec, i)); + } + vector_free(matchvec); + + /* Make new matchvec. */ + matchvec = vector_init(INIT_MATCHVEC_SIZE); + vector_set(matchvec, lcdstr); + match_str = (char **)matchvec->index; + vector_only_wrapper_free(matchvec); + + *status = CMD_COMPLETE_MATCH; + return match_str; + } + } + } + + match_str = (char **)matchvec->index; + vector_only_wrapper_free(matchvec); + *status = CMD_COMPLETE_LIST_MATCH; + return match_str; +} + +char **cmd_complete_command(vector vline, struct vty *vty, int *status) +{ + char **ret; + + if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { + enum node_type onode; + vector shifted_vline; + unsigned int index; + + onode = vty->node; + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ + + shifted_vline = vector_init(vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active(vline); index++) { + vector_set_index(shifted_vline, index - 1, + vector_lookup(vline, index)); + } + + ret = cmd_complete_command_real(shifted_vline, vty, status); + + vector_free(shifted_vline); + vty->node = onode; + return ret; + } + + return cmd_complete_command_real(vline, vty, status); +} + +/* return parent node */ +/* MUST eventually converge on CONFIG_NODE */ +enum node_type vty_go_parent(struct vty *vty) +{ + assert(vty->node > CONFIG_NODE); + + if (vty_go_parent_cb) + vty_go_parent_cb(vty); + else + vty->node = CONFIG_NODE; + + return vty->node; +} + +/* Execute command by argument vline vector. */ +static int +cmd_execute_command_real(vector vline, struct vty *vty, + struct cmd_element **cmd) +{ + unsigned int i; + unsigned int index; + vector cmd_vector; + struct cmd_element *cmd_element; + struct cmd_element *matched_element; + unsigned int matched_count, incomplete_count; + int argc; + const char *argv[CMD_ARGC_MAX]; + enum match_type match = 0; + int varflag; + char *command; + + /* Make copy of command elements. */ + cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); + + for (index = 0; index < vector_active(vline); index++) + if ((command = vector_slot(vline, index))) { + int ret; + + match = + cmd_filter_by_completion(command, cmd_vector, + index); + + if (match == vararg_match) + break; + + ret = + is_cmd_ambiguous(command, cmd_vector, index, match); + + if (ret == 1) { + vector_free(cmd_vector); + return CMD_ERR_AMBIGUOUS; + } else if (ret == 2) { + vector_free(cmd_vector); + return CMD_ERR_NO_MATCH; + } + } + + /* Check matched count. */ + matched_element = NULL; + matched_count = 0; + incomplete_count = 0; + + for (i = 0; i < vector_active(cmd_vector); i++) + if ((cmd_element = vector_slot(cmd_vector, i))) { + if (match == vararg_match + || index >= cmd_element->cmdsize) { + matched_element = cmd_element; +#if 0 + printf("DEBUG: %s\n", cmd_element->string); +#endif + matched_count++; + } else { + incomplete_count++; + } + } + + /* Finish of using cmd_vector. */ + vector_free(cmd_vector); + + /* To execute command, matched_count must be 1. */ + if (matched_count == 0) { + if (incomplete_count) + return CMD_ERR_INCOMPLETE; + else + return CMD_ERR_NO_MATCH; + } + + if (matched_count > 1) + return CMD_ERR_AMBIGUOUS; + + /* Argument treatment */ + varflag = 0; + argc = 0; + + for (i = 0; i < vector_active(vline); i++) { + if (varflag) + argv[argc++] = vector_slot(vline, i); + else { + vector descvec = + vector_slot(matched_element->strvec, i); + + if (vector_active(descvec) == 1) { + struct desc *desc = vector_slot(descvec, 0); + + if (CMD_VARARG(desc->cmd)) + varflag = 1; + + if (varflag || CMD_VARIABLE(desc->cmd) + || CMD_OPTION(desc->cmd)) + argv[argc++] = vector_slot(vline, i); + } else + argv[argc++] = vector_slot(vline, i); + } + + if (argc >= CMD_ARGC_MAX) + return CMD_ERR_EXEED_ARGC_MAX; + } + + /* For vtysh execution. */ + if (cmd) + *cmd = matched_element; + + if (matched_element->daemon) + return CMD_SUCCESS_DAEMON; + + /* Execute matched command. */ + return (*matched_element->func) (matched_element, vty, argc, argv); +} + +int +cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd, + int vtysh) +{ + int ret, saved_ret, tried = 0; + enum node_type onode; + void *oindex; + + onode = vty->node; + oindex = vty->index; + + if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { + vector shifted_vline; + unsigned int index; + + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ + + shifted_vline = vector_init(vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active(vline); index++) { + vector_set_index(shifted_vline, index - 1, + vector_lookup(vline, index)); + } + + ret = cmd_execute_command_real(shifted_vline, vty, cmd); + + vector_free(shifted_vline); + vty->node = onode; + return ret; + } + + saved_ret = ret = cmd_execute_command_real(vline, vty, cmd); + + if (vtysh) + return saved_ret; + + /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ + while (ret != CMD_SUCCESS && ret != CMD_WARNING + && vty->node > CONFIG_NODE) { + vty_go_parent(vty); + ret = cmd_execute_command_real(vline, vty, cmd); + tried = 1; + if (ret == CMD_SUCCESS || ret == CMD_WARNING) { + /* succesfull command, leave the node as is */ + return ret; + } + } + /* no command succeeded, reset the vty to the original node and + return the error for this node */ + if (tried) { + vty->node = onode; + vty->index = oindex; + } + return saved_ret; +} + +/* Execute command by argument readline. */ +int +cmd_execute_command_strict(vector vline, struct vty *vty, + struct cmd_element **cmd) +{ + unsigned int i; + unsigned int index; + vector cmd_vector; + struct cmd_element *cmd_element; + struct cmd_element *matched_element; + unsigned int matched_count, incomplete_count; + int argc; + const char *argv[CMD_ARGC_MAX]; + int varflag; + enum match_type match = 0; + char *command; + + /* Make copy of command element */ + cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); + + for (index = 0; index < vector_active(vline); index++) + if ((command = vector_slot(vline, index))) { + int ret; + + match = cmd_filter_by_string(vector_slot(vline, index), + cmd_vector, index); + + /* If command meets '.VARARG' then finish matching. */ + if (match == vararg_match) + break; + + ret = + is_cmd_ambiguous(command, cmd_vector, index, match); + if (ret == 1) { + vector_free(cmd_vector); + return CMD_ERR_AMBIGUOUS; + } + if (ret == 2) { + vector_free(cmd_vector); + return CMD_ERR_NO_MATCH; + } + } + + /* Check matched count. */ + matched_element = NULL; + matched_count = 0; + incomplete_count = 0; + for (i = 0; i < vector_active(cmd_vector); i++) + if (vector_slot(cmd_vector, i) != NULL) { + cmd_element = vector_slot(cmd_vector, i); + + if (match == vararg_match + || index >= cmd_element->cmdsize) { + matched_element = cmd_element; + matched_count++; + } else + incomplete_count++; + } + + /* Finish of using cmd_vector. */ + vector_free(cmd_vector); + + /* To execute command, matched_count must be 1. */ + if (matched_count == 0) { + if (incomplete_count) + return CMD_ERR_INCOMPLETE; + else + return CMD_ERR_NO_MATCH; + } + + if (matched_count > 1) + return CMD_ERR_AMBIGUOUS; + + /* Argument treatment */ + varflag = 0; + argc = 0; + + for (i = 0; i < vector_active(vline); i++) { + if (varflag) + argv[argc++] = vector_slot(vline, i); + else { + vector descvec = + vector_slot(matched_element->strvec, i); + + if (vector_active(descvec) == 1) { + struct desc *desc = vector_slot(descvec, 0); + + if (CMD_VARARG(desc->cmd)) + varflag = 1; + + if (varflag || CMD_VARIABLE(desc->cmd) + || CMD_OPTION(desc->cmd)) + argv[argc++] = vector_slot(vline, i); + } else + argv[argc++] = vector_slot(vline, i); + } + + if (argc >= CMD_ARGC_MAX) + return CMD_ERR_EXEED_ARGC_MAX; + } + + /* For vtysh execution. */ + if (cmd) + *cmd = matched_element; + + if (matched_element->daemon) + return CMD_SUCCESS_DAEMON; + + /* Now execute matched command */ + return (*matched_element->func) (matched_element, vty, argc, argv); +} + +/* Configration make from file. */ +int config_from_file(struct vty *vty, FILE * fp) +{ + int ret; + vector vline; + + while (fgets(vty->buf, VTY_BUFSIZ, fp)) { + vline = cmd_make_strvec(vty->buf); + + /* In case of comment line */ + if (vline == NULL) + continue; + /* Execute configuration command : this is strict match */ + ret = cmd_execute_command_strict(vline, vty, NULL); + + /* Try again with setting node to CONFIG_NODE */ + while (ret != CMD_SUCCESS && ret != CMD_WARNING + && ret != CMD_ERR_NOTHING_TODO + && vty->node != CONFIG_NODE) { + vty_go_parent(vty); + ret = cmd_execute_command_strict(vline, vty, NULL); + } + + cmd_free_strvec(vline); + + if (ret != CMD_SUCCESS && ret != CMD_WARNING + && ret != CMD_ERR_NOTHING_TODO) + return ret; + } + return CMD_SUCCESS; +} + +/* Configration from terminal */ +DEFUN(config_terminal, + config_terminal_cmd, + "configure terminal", + "Configuration from vty interface\n" "Configuration terminal\n") +{ + if (vty_config_lock(vty)) + vty->node = CONFIG_NODE; + else { + vty_out(vty, "VTY configuration is locked by other VTY%s", + VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +/* Enable command */ +DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") +{ + /* If enable password is NULL, change to ENABLE_NODE */ + if ((host.enable == NULL && host.enable_encrypt == NULL) || + vty->type == VTY_SHELL_SERV) + vty->node = ENABLE_NODE; + else + vty->node = AUTH_ENABLE_NODE; + + return CMD_SUCCESS; +} + +/* Disable command */ +DEFUN(disable, + config_disable_cmd, "disable", "Turn off privileged mode command\n") +{ + if (vty->node == ENABLE_NODE) + vty->node = VIEW_NODE; + return CMD_SUCCESS; +} + +/* Down vty node level. */ +gDEFUN(config_exit, + config_exit_cmd, "exit", "Exit current mode and down to previous mode\n") +{ + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + if (0) //vty_shell (vty)) + exit(0); + else + vty->status = VTY_CLOSE; + break; + case CONFIG_NODE: + vty->node = ENABLE_NODE; + vty_config_unlock(vty); + break; + case VTY_NODE: + vty->node = CONFIG_NODE; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* End of configuration. */ + gDEFUN(config_end, + config_end_cmd, "end", "End current mode and change to enable mode.") +{ + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case VTY_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* Show version. */ +DEFUN(show_version, + show_version_cmd, "show version", SHOW_STR "Displays program version\n") +{ + vty_out(vty, "%s %s (%s).%s", host.prog_name, host.prog_version, + host.name ? host.name : "", VTY_NEWLINE); + vty_out(vty, "%s%s", host.prog_copyright, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +/* Help display function for all node. */ +gDEFUN(config_help, + config_help_cmd, "help", "Description of the interactive help system\n") +{ + vty_out(vty, + "This VTY provides advanced help features. When you need help,%s\ +anytime at the command line please press '?'.%s\ +%s\ +If nothing matches, the help list will be empty and you must backup%s\ + until entering a '?' shows the available options.%s\ +Two styles of help are provided:%s\ +1. Full help is available when you are ready to enter a%s\ +command argument (e.g. 'show ?') and describes each possible%s\ +argument.%s\ +2. Partial help is provided when an abbreviated argument is entered%s\ + and you want to know what arguments match the input%s\ + (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Help display function for all node. */ +gDEFUN(config_list, config_list_cmd, "list", "Print command list\n") +{ + unsigned int i; + struct cmd_node *cnode = vector_slot(cmdvec, vty->node); + struct cmd_element *cmd; + + for (i = 0; i < vector_active(cnode->cmd_vector); i++) + if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL + && !(cmd->attr == CMD_ATTR_DEPRECATED + || cmd->attr == CMD_ATTR_HIDDEN)) + vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Write current configuration into file. */ +DEFUN(config_write_file, + config_write_file_cmd, + "write file", + "Write running configuration to memory, network, or terminal\n" + "Write to configuration file\n") +{ + unsigned int i; + int fd; + struct cmd_node *node; + char *config_file; + char *config_file_tmp = NULL; + char *config_file_sav = NULL; + struct vty *file_vty; + + /* Check and see if we are operating under vtysh configuration */ + if (host.config == NULL) { + vty_out(vty, "Can't save to configuration file, using vtysh.%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get filename. */ + config_file = host.config; + + config_file_sav = + _talloc_zero(tall_vty_cmd_ctx, + strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1, + "config_file_sav"); + strcpy(config_file_sav, config_file); + strcat(config_file_sav, CONF_BACKUP_EXT); + + config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8, + "config_file_tmp"); + sprintf(config_file_tmp, "%s.XXXXXX", config_file); + + /* Open file to configuration write. */ + fd = mkstemp(config_file_tmp); + if (fd < 0) { + vty_out(vty, "Can't open configuration file %s.%s", + config_file_tmp, VTY_NEWLINE); + talloc_free(config_file_tmp); + talloc_free(config_file_sav); + return CMD_WARNING; + } + + /* Make vty for configuration file. */ + file_vty = vty_new(); + file_vty->fd = fd; + file_vty->type = VTY_FILE; + + /* Config file header print. */ + vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!", + host.prog_name, host.prog_version); + //vty_time_print (file_vty, 1); + vty_out(file_vty, "!\n"); + + for (i = 0; i < vector_active(cmdvec); i++) + if ((node = vector_slot(cmdvec, i)) && node->func) { + if ((*node->func) (file_vty)) + vty_out(file_vty, "!\n"); + } + vty_close(file_vty); + + if (unlink(config_file_sav) != 0) + if (errno != ENOENT) { + vty_out(vty, + "Can't unlink backup configuration file %s.%s", + config_file_sav, VTY_NEWLINE); + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + unlink(config_file_tmp); + return CMD_WARNING; + } + if (link(config_file, config_file_sav) != 0) { + vty_out(vty, "Can't backup old configuration file %s.%s", + config_file_sav, VTY_NEWLINE); + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + unlink(config_file_tmp); + return CMD_WARNING; + } + sync(); + if (unlink(config_file) != 0) { + vty_out(vty, "Can't unlink configuration file %s.%s", + config_file, VTY_NEWLINE); + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + unlink(config_file_tmp); + return CMD_WARNING; + } + if (link(config_file_tmp, config_file) != 0) { + vty_out(vty, "Can't save configuration file %s.%s", config_file, + VTY_NEWLINE); + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + unlink(config_file_tmp); + return CMD_WARNING; + } + unlink(config_file_tmp); + sync(); + + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + + if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) { + vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s", + config_file, strerror(errno), errno, VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out(vty, "Configuration saved to %s%s", config_file, VTY_NEWLINE); + return CMD_SUCCESS; +} + +ALIAS(config_write_file, + config_write_cmd, + "write", "Write running configuration to memory, network, or terminal\n") + + ALIAS(config_write_file, + config_write_memory_cmd, + "write memory", + "Write running configuration to memory, network, or terminal\n" + "Write configuration to the file (same as write file)\n") + + ALIAS(config_write_file, + copy_runningconfig_startupconfig_cmd, + "copy running-config startup-config", + "Copy configuration\n" + "Copy running config to... \n" + "Copy running config to startup config (same as write file)\n") + +/* Write current configuration into the terminal. */ + DEFUN(config_write_terminal, + config_write_terminal_cmd, + "write terminal", + "Write running configuration to memory, network, or terminal\n" + "Write to terminal\n") +{ + unsigned int i; + struct cmd_node *node; + + if (vty->type == VTY_SHELL_SERV) { + for (i = 0; i < vector_active(cmdvec); i++) + if ((node = vector_slot(cmdvec, i)) && node->func + && node->vtysh) { + if ((*node->func) (vty)) + vty_out(vty, "!%s", VTY_NEWLINE); + } + } else { + vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE, + VTY_NEWLINE); + vty_out(vty, "!%s", VTY_NEWLINE); + + for (i = 0; i < vector_active(cmdvec); i++) + if ((node = vector_slot(cmdvec, i)) && node->func) { + if ((*node->func) (vty)) + vty_out(vty, "!%s", VTY_NEWLINE); + } + vty_out(vty, "end%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +/* Write current configuration into the terminal. */ +ALIAS(config_write_terminal, + show_running_config_cmd, + "show running-config", SHOW_STR "running configuration\n") + +/* Write startup configuration into the terminal. */ + DEFUN(show_startup_config, + show_startup_config_cmd, + "show startup-config", SHOW_STR "Contentes of startup configuration\n") +{ + char buf[BUFSIZ]; + FILE *confp; + + confp = fopen(host.config, "r"); + if (confp == NULL) { + vty_out(vty, "Can't open configuration file [%s]%s", + host.config, VTY_NEWLINE); + return CMD_WARNING; + } + + while (fgets(buf, BUFSIZ, confp)) { + char *cp = buf; + + while (*cp != '\r' && *cp != '\n' && *cp != '\0') + cp++; + *cp = '\0'; + + vty_out(vty, "%s%s", buf, VTY_NEWLINE); + } + + fclose(confp); + + return CMD_SUCCESS; +} + +/* Hostname configuration */ +DEFUN(config_hostname, + hostname_cmd, + "hostname WORD", + "Set system's network name\n" "This system's network name\n") +{ + if (!isalpha((int)*argv[0])) { + vty_out(vty, "Please specify string starting with alphabet%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.name) + talloc_free(host.name); + + host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(config_no_hostname, + no_hostname_cmd, + "no hostname [HOSTNAME]", + NO_STR "Reset system's network name\n" "Host name of this router\n") +{ + if (host.name) + talloc_free(host.name); + host.name = NULL; + return CMD_SUCCESS; +} + +/* VTY interface password set. */ +DEFUN(config_password, password_cmd, + "password (8|) WORD", + "Assign the terminal connection password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" "The HIDDEN line password string\n") +{ + /* Argument check. */ + if (argc == 0) { + vty_out(vty, "Please specify password.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc == 2) { + if (*argv[0] == '8') { + if (host.password) + talloc_free(host.password); + host.password = NULL; + if (host.password_encrypt) + talloc_free(host.password_encrypt); + host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]); + return CMD_SUCCESS; + } else { + vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (!isalnum((int)*argv[0])) { + vty_out(vty, + "Please specify string starting with alphanumeric%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.password) + talloc_free(host.password); + host.password = NULL; + +#ifdef VTY_CRYPT_PW + if (host.encrypt) { + if (host.password_encrypt) + talloc_free(host.password_encrypt); + host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0])); + } else +#endif + host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS(config_password, password_text_cmd, + "password LINE", + "Assign the terminal connection password\n" + "The UNENCRYPTED (cleartext) line password\n") + +/* VTY enable password set. */ + DEFUN(config_enable_password, enable_password_cmd, + "enable password (8|) WORD", + "Modify enable password parameters\n" + "Assign the privileged level password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" "The HIDDEN 'enable' password string\n") +{ + /* Argument check. */ + if (argc == 0) { + vty_out(vty, "Please specify password.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Crypt type is specified. */ + if (argc == 2) { + if (*argv[0] == '8') { + if (host.enable) + talloc_free(host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]); + + return CMD_SUCCESS; + } else { + vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (!isalnum((int)*argv[0])) { + vty_out(vty, + "Please specify string starting with alphanumeric%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.enable) + talloc_free(host.enable); + host.enable = NULL; + + /* Plain password input. */ +#ifdef VTY_CRYPT_PW + if (host.encrypt) { + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0])); + } else +#endif + host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS(config_enable_password, + enable_password_text_cmd, + "enable password LINE", + "Modify enable password parameters\n" + "Assign the privileged level password\n" + "The UNENCRYPTED (cleartext) 'enable' password\n") + +/* VTY enable password delete. */ + DEFUN(no_config_enable_password, no_enable_password_cmd, + "no enable password", + NO_STR + "Modify enable password parameters\n" + "Assign the privileged level password\n") +{ + if (host.enable) + talloc_free(host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = NULL; + + return CMD_SUCCESS; +} + +#ifdef VTY_CRYPT_PW +DEFUN(service_password_encrypt, + service_password_encrypt_cmd, + "service password-encryption", + "Set up miscellaneous service\n" "Enable encrypted passwords\n") +{ + if (host.encrypt) + return CMD_SUCCESS; + + host.encrypt = 1; + + if (host.password) { + if (host.password_encrypt) + talloc_free(host.password_encrypt); + host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password)); + } + if (host.enable) { + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable)); + } + + return CMD_SUCCESS; +} + +DEFUN(no_service_password_encrypt, + no_service_password_encrypt_cmd, + "no service password-encryption", + NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n") +{ + if (!host.encrypt) + return CMD_SUCCESS; + + host.encrypt = 0; + + if (host.password_encrypt) + talloc_free(host.password_encrypt); + host.password_encrypt = NULL; + + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = NULL; + + return CMD_SUCCESS; +} +#endif + +DEFUN(config_terminal_length, config_terminal_length_cmd, + "terminal length <0-512>", + "Set terminal line parameters\n" + "Set number of lines on a screen\n" + "Number of lines on screen (0 for no pausing)\n") +{ + int lines; + char *endptr = NULL; + + lines = strtol(argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') { + vty_out(vty, "length is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + vty->lines = lines; + + return CMD_SUCCESS; +} + +DEFUN(config_terminal_no_length, config_terminal_no_length_cmd, + "terminal no length", + "Set terminal line parameters\n" + NO_STR "Set number of lines on a screen\n") +{ + vty->lines = -1; + return CMD_SUCCESS; +} + +DEFUN(service_terminal_length, service_terminal_length_cmd, + "service terminal-length <0-512>", + "Set up miscellaneous service\n" + "System wide terminal length configuration\n" + "Number of lines of VTY (0 means no line control)\n") +{ + int lines; + char *endptr = NULL; + + lines = strtol(argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') { + vty_out(vty, "length is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + host.lines = lines; + + return CMD_SUCCESS; +} + +DEFUN(no_service_terminal_length, no_service_terminal_length_cmd, + "no service terminal-length [<0-512>]", + NO_STR + "Set up miscellaneous service\n" + "System wide terminal length configuration\n" + "Number of lines of VTY (0 means no line control)\n") +{ + host.lines = -1; + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(do_echo, + echo_cmd, + "echo .MESSAGE", + "Echo a message back to the vty\n" "The message to echo\n") +{ + char *message; + + vty_out(vty, "%s%s", + ((message = + argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE); + if (message) + talloc_free(message); + return CMD_SUCCESS; +} + +#if 0 +DEFUN(config_logmsg, + config_logmsg_cmd, + "logmsg " LOG_LEVELS " .MESSAGE", + "Send a message to enabled logging destinations\n" + LOG_LEVEL_DESC "The message to send\n") +{ + int level; + char *message; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + + zlog(NULL, level, + ((message = argv_concat(argv, argc, 1)) ? message : "")); + if (message) + talloc_free(message); + return CMD_SUCCESS; +} + +DEFUN(show_logging, + show_logging_cmd, + "show logging", SHOW_STR "Show current logging configuration\n") +{ + struct zlog *zl = zlog_default; + + vty_out(vty, "Syslog logging: "); + if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s, facility %s, ident %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], + facility_name(zl->facility), zl->ident); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Stdout logging: "); + if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Monitor logging: "); + if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "File logging: "); + if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s, filename %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], + zl->filename); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Protocol name: %s%s", + zlog_proto_names[zl->protocol], VTY_NEWLINE); + vty_out(vty, "Record priority: %s%s", + (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(config_log_stdout, + config_log_stdout_cmd, + "log stdout", "Logging control\n" "Set stdout logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl); + return CMD_SUCCESS; +} + +DEFUN(config_log_stdout_level, + config_log_stdout_level_cmd, + "log stdout " LOG_LEVELS, + "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level(NULL, ZLOG_DEST_STDOUT, level); + return CMD_SUCCESS; +} + +DEFUN(no_config_log_stdout, + no_config_log_stdout_cmd, + "no log stdout [LEVEL]", + NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED); + return CMD_SUCCESS; +} + +DEFUN(config_log_monitor, + config_log_monitor_cmd, + "log monitor", + "Logging control\n" "Set terminal line (monitor) logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl); + return CMD_SUCCESS; +} + +DEFUN(config_log_monitor_level, + config_log_monitor_level_cmd, + "log monitor " LOG_LEVELS, + "Logging control\n" + "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level(NULL, ZLOG_DEST_MONITOR, level); + return CMD_SUCCESS; +} + +DEFUN(no_config_log_monitor, + no_config_log_monitor_cmd, + "no log monitor [LEVEL]", + NO_STR + "Logging control\n" + "Disable terminal line (monitor) logging\n" "Logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); + return CMD_SUCCESS; +} + +static int set_log_file(struct vty *vty, const char *fname, int loglevel) +{ + int ret; + char *p = NULL; + const char *fullpath; + + /* Path detection. */ + if (!IS_DIRECTORY_SEP(*fname)) { + char cwd[MAXPATHLEN + 1]; + cwd[MAXPATHLEN] = '\0'; + + if (getcwd(cwd, MAXPATHLEN) == NULL) { + zlog_err("config_log_file: Unable to alloc mem!"); + return CMD_WARNING; + } + + if ((p = _talloc_zero(tall_vcmd_ctx, + strlen(cwd) + strlen(fname) + 2), + "set_log_file") + == NULL) { + zlog_err("config_log_file: Unable to alloc mem!"); + return CMD_WARNING; + } + sprintf(p, "%s/%s", cwd, fname); + fullpath = p; + } else + fullpath = fname; + + ret = zlog_set_file(NULL, fullpath, loglevel); + + if (p) + talloc_free(p); + + if (!ret) { + vty_out(vty, "can't open logfile %s\n", fname); + return CMD_WARNING; + } + + if (host.logfile) + talloc_free(host.logfile); + + host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname); + + return CMD_SUCCESS; +} + +DEFUN(config_log_file, + config_log_file_cmd, + "log file FILENAME", + "Logging control\n" "Logging to file\n" "Logging filename\n") +{ + return set_log_file(vty, argv[0], zlog_default->default_lvl); +} + +DEFUN(config_log_file_level, + config_log_file_level_cmd, + "log file FILENAME " LOG_LEVELS, + "Logging control\n" + "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[1])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + return set_log_file(vty, argv[0], level); +} + +DEFUN(no_config_log_file, + no_config_log_file_cmd, + "no log file [FILENAME]", + NO_STR + "Logging control\n" "Cancel logging to file\n" "Logging file name\n") +{ + zlog_reset_file(NULL); + + if (host.logfile) + talloc_free(host.logfile); + + host.logfile = NULL; + + return CMD_SUCCESS; +} + +ALIAS(no_config_log_file, + no_config_log_file_level_cmd, + "no log file FILENAME LEVEL", + NO_STR + "Logging control\n" + "Cancel logging to file\n" "Logging file name\n" "Logging level\n") + + DEFUN(config_log_syslog, + config_log_syslog_cmd, + "log syslog", "Logging control\n" "Set syslog logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); + return CMD_SUCCESS; +} + +DEFUN(config_log_syslog_level, + config_log_syslog_level_cmd, + "log syslog " LOG_LEVELS, + "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level); + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED(config_log_syslog_facility, + config_log_syslog_facility_cmd, + "log syslog facility " LOG_FACILITIES, + "Logging control\n" + "Logging goes to syslog\n" + "(Deprecated) Facility parameter for syslog messages\n" + LOG_FACILITY_DESC) +{ + int facility; + + if ((facility = facility_match(argv[0])) < 0) + return CMD_ERR_NO_MATCH; + + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); + zlog_default->facility = facility; + return CMD_SUCCESS; +} + +DEFUN(no_config_log_syslog, + no_config_log_syslog_cmd, + "no log syslog [LEVEL]", + NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED); + return CMD_SUCCESS; +} + +ALIAS(no_config_log_syslog, + no_config_log_syslog_facility_cmd, + "no log syslog facility " LOG_FACILITIES, + NO_STR + "Logging control\n" + "Logging goes to syslog\n" + "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) + + DEFUN(config_log_facility, + config_log_facility_cmd, + "log facility " LOG_FACILITIES, + "Logging control\n" + "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) +{ + int facility; + + if ((facility = facility_match(argv[0])) < 0) + return CMD_ERR_NO_MATCH; + zlog_default->facility = facility; + return CMD_SUCCESS; +} + +DEFUN(no_config_log_facility, + no_config_log_facility_cmd, + "no log facility [FACILITY]", + NO_STR + "Logging control\n" + "Reset syslog facility to default (daemon)\n" "Syslog facility\n") +{ + zlog_default->facility = LOG_DAEMON; + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED(config_log_trap, + config_log_trap_cmd, + "log trap " LOG_LEVELS, + "Logging control\n" + "(Deprecated) Set logging level and default for all destinations\n" + LOG_LEVEL_DESC) +{ + int new_level; + int i; + + if ((new_level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + + zlog_default->default_lvl = new_level; + for (i = 0; i < ZLOG_NUM_DESTS; i++) + if (zlog_default->maxlvl[i] != ZLOG_DISABLED) + zlog_default->maxlvl[i] = new_level; + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED(no_config_log_trap, + no_config_log_trap_cmd, + "no log trap [LEVEL]", + NO_STR + "Logging control\n" + "Permit all logging information\n" "Logging level\n") +{ + zlog_default->default_lvl = LOG_DEBUG; + return CMD_SUCCESS; +} + +DEFUN(config_log_record_priority, + config_log_record_priority_cmd, + "log record-priority", + "Logging control\n" + "Log the priority of the message within the message\n") +{ + zlog_default->record_priority = 1; + return CMD_SUCCESS; +} + +DEFUN(no_config_log_record_priority, + no_config_log_record_priority_cmd, + "no log record-priority", + NO_STR + "Logging control\n" + "Do not log the priority of the message within the message\n") +{ + zlog_default->record_priority = 0; + return CMD_SUCCESS; +} +#endif + +DEFUN(banner_motd_file, + banner_motd_file_cmd, + "banner motd file [FILE]", + "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n") +{ + if (host.motdfile) + talloc_free(host.motdfile); + host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(banner_motd_default, + banner_motd_default_cmd, + "banner motd default", + "Set banner string\n" "Strings for motd\n" "Default string\n") +{ + host.motd = default_motd; + return CMD_SUCCESS; +} + +DEFUN(no_banner_motd, + no_banner_motd_cmd, + "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n") +{ + host.motd = NULL; + if (host.motdfile) + talloc_free(host.motdfile); + host.motdfile = NULL; + return CMD_SUCCESS; +} + +/* Set config filename. Called from vty.c */ +void host_config_set(const char *filename) +{ + host.config = talloc_strdup(tall_vty_cmd_ctx, filename); +} + +void install_default(enum node_type node) +{ + install_element(node, &config_help_cmd); + install_element(node, &config_list_cmd); + + install_element(node, &config_write_terminal_cmd); + install_element(node, &config_write_file_cmd); + install_element(node, &config_write_memory_cmd); + install_element(node, &config_write_cmd); + install_element(node, &show_running_config_cmd); +} + +/* Initialize command interface. Install basic nodes and commands. */ +void cmd_init(int terminal) +{ + /* Allocate initial top vector of commands. */ + cmdvec = vector_init(VECTOR_MIN_SIZE); + + /* Default host value settings. */ + host.name = NULL; + host.password = NULL; + host.enable = NULL; + host.logfile = NULL; + host.config = NULL; + host.lines = -1; + host.motd = default_motd; + host.motdfile = NULL; + + /* Install top nodes. */ + install_node(&view_node, NULL); + install_node(&enable_node, NULL); + install_node(&auth_node, NULL); + install_node(&auth_enable_node, NULL); + install_node(&config_node, config_write_host); + + /* Each node's basic commands. */ + install_element(VIEW_NODE, &show_version_cmd); + if (terminal) { + install_element(VIEW_NODE, &config_list_cmd); + install_element(VIEW_NODE, &config_exit_cmd); + install_element(VIEW_NODE, &config_help_cmd); + install_element(VIEW_NODE, &config_enable_cmd); + install_element(VIEW_NODE, &config_terminal_length_cmd); + install_element(VIEW_NODE, &config_terminal_no_length_cmd); + install_element(VIEW_NODE, &echo_cmd); + } + + if (terminal) { + install_element(ENABLE_NODE, &config_exit_cmd); + install_default(ENABLE_NODE); + install_element(ENABLE_NODE, &config_disable_cmd); + install_element(ENABLE_NODE, &config_terminal_cmd); + install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); + } + install_element (ENABLE_NODE, &show_startup_config_cmd); + install_element(ENABLE_NODE, &show_version_cmd); + + if (terminal) { + install_element(ENABLE_NODE, &config_terminal_length_cmd); + install_element(ENABLE_NODE, &config_terminal_no_length_cmd); + install_element(ENABLE_NODE, &echo_cmd); + + install_default(CONFIG_NODE); + install_element(CONFIG_NODE, &config_exit_cmd); + } + + install_element(CONFIG_NODE, &hostname_cmd); + install_element(CONFIG_NODE, &no_hostname_cmd); + + if (terminal) { + install_element(CONFIG_NODE, &password_cmd); + install_element(CONFIG_NODE, &password_text_cmd); + install_element(CONFIG_NODE, &enable_password_cmd); + install_element(CONFIG_NODE, &enable_password_text_cmd); + install_element(CONFIG_NODE, &no_enable_password_cmd); + +#ifdef VTY_CRYPT_PW + install_element(CONFIG_NODE, &service_password_encrypt_cmd); + install_element(CONFIG_NODE, &no_service_password_encrypt_cmd); +#endif + install_element(CONFIG_NODE, &banner_motd_default_cmd); + install_element(CONFIG_NODE, &banner_motd_file_cmd); + install_element(CONFIG_NODE, &no_banner_motd_cmd); + install_element(CONFIG_NODE, &service_terminal_length_cmd); + install_element(CONFIG_NODE, &no_service_terminal_length_cmd); + + } + srand(time(NULL)); +} diff --git a/src/vty/logging_vty.c b/src/vty/logging_vty.c new file mode 100644 index 00000000..a7871963 --- /dev/null +++ b/src/vty/logging_vty.c @@ -0,0 +1,347 @@ +/* OpenBSC logging helper for the VTY */ +/* (C) 2009-2010 by Harald Welte + * (C) 2009-2010 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +#include +#include + +//#include + +#include +#include +#include +#include +#include + +extern const struct log_info *log_info; + +static void _vty_output(struct log_target *tgt, const char *line) +{ + struct vty *vty = tgt->tgt_vty.vty; + vty_out(vty, "%s", line); + /* This is an ugly hack, but there is no easy way... */ + if (strchr(line, '\n')) + vty_out(vty, "\r"); +} + +struct log_target *log_target_create_vty(struct vty *vty) +{ + struct log_target *target; + + target = log_target_create(); + if (!target) + return NULL; + + target->tgt_vty.vty = vty; + target->output = _vty_output; + return target; +} + +DEFUN(enable_logging, + enable_logging_cmd, + "logging enable", + LOGGING_STR + "Enables logging to this vty\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (conn->dbg) { + vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + conn->dbg = log_target_create_vty(vty); + if (!conn->dbg) + return CMD_WARNING; + + log_add_target(conn->dbg); + return CMD_SUCCESS; +} + +DEFUN(logging_fltr_all, + logging_fltr_all_cmd, + "logging filter all (0|1)", + LOGGING_STR FILTER_STR + "Do you want to log all messages?\n" + "Only print messages matched by other filters\n" + "Bypass filter and print all messages\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + log_set_all_filter(conn->dbg, atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(logging_use_clr, + logging_use_clr_cmd, + "logging color (0|1)", + LOGGING_STR "Configure color-printing for log messages\n" + "Don't use color for printing messages\n" + "Use color for printing messages\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + log_set_use_color(conn->dbg, atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(logging_prnt_timestamp, + logging_prnt_timestamp_cmd, + "logging timestamp (0|1)", + LOGGING_STR "Configure log message timestamping\n" + "Don't prefix each log message\n" + "Prefix each log message with current timestamp\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + log_set_print_timestamp(conn->dbg, atoi(argv[0])); + return CMD_SUCCESS; +} + +/* FIXME: those have to be kept in sync with the log levels and categories */ +#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref|gprs|ns|bssgp|llc|sndcp|all)" +#define CATEGORIES_HELP \ + "A-bis Radio Link Layer (RLL)\n" \ + "Layer3 Call Control (CC)\n" \ + "Layer3 Mobility Management (MM)\n" \ + "Layer3 Radio Resource (RR)\n" \ + "A-bis Radio Signalling Link (RSL)\n" \ + "A-bis Network Management / O&M (NM/OML)\n" \ + "Layer3 Short Messagaging Service (SMS)\n" \ + "Paging Subsystem\n" \ + "MNCC API for Call Control application\n" \ + "A-bis Input Subsystem\n" \ + "A-bis Input Driver for Signalling\n" \ + "A-bis Input Driver for B-Channel (voice data)\n" \ + "A-bis B-Channel / Sub-channel Multiplexer\n" \ + "Radio Measurements\n" \ + "SCCP\n" \ + "Mobile Switching Center\n" \ + "Media Gateway Control Protocol\n" \ + "Hand-over\n" \ + "Database Layer\n" \ + "Reference Counting\n" \ + "GPRS Core\n" \ + "GPRS Network Service (NS)\n" \ + "GPRS BSS Gateway Protocol (BSSGP)\n" \ + "GPRS Logical Link Control Protocol (LLC)\n" \ + "GPRS Sub-Network Dependent Control Protocol (SNDCP)\n" \ + "Global setting for all subsytems\n" + +#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)" +#define LEVELS_HELP \ + "Log simply everything\n" \ + "Log debug messages and higher levels\n" \ + "Log informational messages and higher levels\n" \ + "Log noticable messages and higher levels\n" \ + "Log error messages and higher levels\n" \ + "Log only fatal messages\n" +DEFUN(logging_level, + logging_level_cmd, + "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS, + LOGGING_STR + "Set the log level for a specified category\n" + CATEGORIES_HELP + LEVELS_HELP) +{ + struct telnet_connection *conn; + int category = log_parse_category(argv[0]); + int level = log_parse_level(argv[1]); + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (level < 0) { + vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check for special case where we want to set global log level */ + if (!strcmp(argv[0], "all")) { + log_set_log_level(conn->dbg, level); + return CMD_SUCCESS; + } + + if (category < 0) { + vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + conn->dbg->categories[category].enabled = 1; + conn->dbg->categories[category].loglevel = level; + + return CMD_SUCCESS; +} + +DEFUN(logging_set_category_mask, + logging_set_category_mask_cmd, + "logging set log mask MASK", + LOGGING_STR + "Decide which categories to output.\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + log_parse_category_mask(conn->dbg, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(diable_logging, + disable_logging_cmd, + "logging disable", + LOGGING_STR + "Disables logging to this vty\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + log_del_target(conn->dbg); + talloc_free(conn->dbg); + conn->dbg = NULL; + return CMD_SUCCESS; +} + +static void vty_print_logtarget(struct vty *vty, const struct log_info *info, + const struct log_target *tgt) +{ + unsigned int i; + + vty_out(vty, " Global Loglevel: %s%s", + log_level_str(tgt->loglevel), VTY_NEWLINE); + vty_out(vty, " Use color: %s, Print Timestamp: %s%s", + tgt->use_color ? "On" : "Off", + tgt->print_timestamp ? "On" : "Off", VTY_NEWLINE); + + vty_out(vty, " Log Level specific information:%s", VTY_NEWLINE); + + for (i = 0; i < info->num_cat; i++) { + const struct log_category *cat = &tgt->categories[i]; + vty_out(vty, " %-10s %-10s %-8s %s%s", + info->cat[i].name+1, log_level_str(cat->loglevel), + cat->enabled ? "Enabled" : "Disabled", + info->cat[i].description, + VTY_NEWLINE); + } +} + +#define SHOW_LOG_STR "Show current logging configuration\n" + +DEFUN(show_logging_vty, + show_logging_vty_cmd, + "show logging vty", + SHOW_STR SHOW_LOG_STR + "Show current logging configuration for this vty\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) { + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + vty_print_logtarget(vty, log_info, conn->dbg); + + return CMD_SUCCESS; +} + +gDEFUN(cfg_description, cfg_description_cmd, + "description .TEXT", + "Save human-readable decription of the object\n") +{ + char **dptr = vty->index_sub; + + if (!dptr) { + vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE); + return CMD_WARNING; + } + + *dptr = argv_concat(argv, argc, 0); + if (!dptr) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +gDEFUN(cfg_no_description, cfg_no_description_cmd, + "no description", + NO_STR + "Remove description of the object\n") +{ + char **dptr = vty->index_sub; + + if (!dptr) { + vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (*dptr) { + talloc_free(*dptr); + *dptr = NULL; + } + + return CMD_SUCCESS; +} + +void logging_vty_add_cmds() +{ + install_element_ve(&enable_logging_cmd); + install_element_ve(&disable_logging_cmd); + install_element_ve(&logging_fltr_all_cmd); + install_element_ve(&logging_use_clr_cmd); + install_element_ve(&logging_prnt_timestamp_cmd); + install_element_ve(&logging_set_category_mask_cmd); + install_element_ve(&logging_level_cmd); + install_element_ve(&show_logging_vty_cmd); +} diff --git a/src/vty/telnet_interface.c b/src/vty/telnet_interface.c new file mode 100644 index 00000000..90690960 --- /dev/null +++ b/src/vty/telnet_interface.c @@ -0,0 +1,203 @@ +/* minimalistic telnet/network interface it might turn into a wire interface */ +/* (C) 2009 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* per connection data */ +LLIST_HEAD(active_connections); + +static void *tall_telnet_ctx; + +/* per network data */ +static int telnet_new_connection(struct bsc_fd *fd, unsigned int what); + +static struct bsc_fd server_socket = { + .when = BSC_FD_READ, + .cb = telnet_new_connection, + .priv_nr = 0, +}; + +int telnet_init(void *tall_ctx, void *priv, int port) +{ + struct sockaddr_in sock_addr; + int fd, rc, on = 1; + + tall_telnet_ctx = talloc_named_const(tall_ctx, 1, + "telnet_connection"); + + fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (fd < 0) { + LOGP(0, LOGL_ERROR, "Telnet interface socket creation failed\n"); + return fd; + } + + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_port = htons(port); + sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + rc = bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (bind < 0) { + LOGP(0, LOGL_ERROR, "Telnet interface failed to bind\n"); + close(fd); + return rc; + } + + rc = listen(fd, 0); + if (rc < 0) { + LOGP(0, LOGL_ERROR, "Telnet interface failed to listen\n"); + close(fd); + return rc; + } + + server_socket.data = priv; + server_socket.fd = fd; + bsc_register_fd(&server_socket); + + return 0; +} + +extern const char *openbsc_copyright; + +static void print_welcome(int fd) +{ + int ret; + static char *msg = + "Welcome to the OpenBSC Control interface\n"; + + ret = write(fd, msg, strlen(msg)); + ret = write(fd, openbsc_copyright, strlen(openbsc_copyright)); +} + +int telnet_close_client(struct bsc_fd *fd) +{ + struct telnet_connection *conn = (struct telnet_connection*)fd->data; + + close(fd->fd); + bsc_unregister_fd(fd); + + if (conn->dbg) { + log_del_target(conn->dbg); + talloc_free(conn->dbg); + } + + llist_del(&conn->entry); + talloc_free(conn); + return 0; +} + +static int client_data(struct bsc_fd *fd, unsigned int what) +{ + struct telnet_connection *conn = fd->data; + int rc = 0; + + if (what & BSC_FD_READ) { + conn->fd.when &= ~BSC_FD_READ; + rc = vty_read(conn->vty); + } + + /* vty might have been closed from vithin vty_read() */ + if (!conn->vty) + return rc; + + if (what & BSC_FD_WRITE) { + rc = buffer_flush_all(conn->vty->obuf, fd->fd); + if (rc == BUFFER_EMPTY) + conn->fd.when &= ~BSC_FD_WRITE; + } + + return rc; +} + +static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) +{ + struct telnet_connection *connection; + struct sockaddr_in sockaddr; + socklen_t len = sizeof(sockaddr); + int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len); + + if (new_connection < 0) { + LOGP(0, LOGL_ERROR, "telnet accept failed\n"); + return new_connection; + } + + connection = talloc_zero(tall_telnet_ctx, struct telnet_connection); + connection->priv = fd->data; + connection->fd.data = connection; + connection->fd.fd = new_connection; + connection->fd.when = BSC_FD_READ; + connection->fd.cb = client_data; + bsc_register_fd(&connection->fd); + llist_add_tail(&connection->entry, &active_connections); + + print_welcome(new_connection); + + connection->vty = vty_create(new_connection, connection); + if (!connection->vty) { + LOGP(0, LOGL_ERROR, "couldn't create VTY\n"); + close(new_connection); + talloc_free(connection); + return -1; + } + + return 0; +} + +/* callback from VTY code */ +void vty_event(enum event event, int sock, struct vty *vty) +{ + struct telnet_connection *connection = vty->priv; + struct bsc_fd *bfd = &connection->fd; + + if (vty->type != VTY_TERM) + return; + + switch (event) { + case VTY_READ: + bfd->when |= BSC_FD_READ; + break; + case VTY_WRITE: + bfd->when |= BSC_FD_WRITE; + break; + case VTY_CLOSED: + /* vty layer is about to free() vty */ + connection->vty = NULL; + telnet_close_client(bfd); + break; + default: + break; + } +} + diff --git a/src/vty/utils.c b/src/vty/utils.c new file mode 100644 index 00000000..e163526e --- /dev/null +++ b/src/vty/utils.c @@ -0,0 +1,50 @@ +/* utility routines for printing common objects in the Osmocom world */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +#include +#include +#include +#include + +#include + +void vty_out_rate_ctr_group(struct vty *vty, const char *prefix, + struct rate_ctr_group *ctrg) +{ + unsigned int i; + + vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE); + for (i = 0; i < ctrg->desc->num_ctr; i++) { + struct rate_ctr *ctr = &ctrg->ctr[i]; + vty_out(vty, " %s%s: %8" PRIu64 " " + "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s", + prefix, ctrg->desc->ctr_desc[i].description, ctr->current, + ctr->intv[RATE_CTR_INTV_SEC].rate, + ctr->intv[RATE_CTR_INTV_MIN].rate, + ctr->intv[RATE_CTR_INTV_HOUR].rate, + ctr->intv[RATE_CTR_INTV_DAY].rate, + VTY_NEWLINE); + }; +} diff --git a/src/vty/vector.c b/src/vty/vector.c new file mode 100644 index 00000000..0343163f --- /dev/null +++ b/src/vty/vector.c @@ -0,0 +1,192 @@ +/* Generic vector interface routine + * Copyright (C) 1997 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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 2, or (at your option) any + * later version. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include + +#include +#include +#include +#include + +void *tall_vty_vec_ctx; + +/* Initialize vector : allocate memory and return vector. */ +vector vector_init(unsigned int size) +{ + vector v = talloc_zero(tall_vty_vec_ctx, struct _vector); + if (!v) + return NULL; + + /* allocate at least one slot */ + if (size == 0) + size = 1; + + v->alloced = size; + v->active = 0; + v->index = _talloc_zero(tall_vty_vec_ctx, sizeof(void *) * size, + "vector_init:index"); + if (!v->index) { + talloc_free(v); + return NULL; + } + return v; +} + +void vector_only_wrapper_free(vector v) +{ + talloc_free(v); +} + +void vector_only_index_free(void *index) +{ + talloc_free(index); +} + +void vector_free(vector v) +{ + talloc_free(v->index); + talloc_free(v); +} + +vector vector_copy(vector v) +{ + unsigned int size; + vector new = talloc_zero(tall_vty_vec_ctx, struct _vector); + if (!new) + return NULL; + + new->active = v->active; + new->alloced = v->alloced; + + size = sizeof(void *) * (v->alloced); + new->index = _talloc_zero(tall_vty_vec_ctx, size, "vector_copy:index"); + if (!new->index) { + talloc_free(new); + return NULL; + } + memcpy(new->index, v->index, size); + + return new; +} + +/* Check assigned index, and if it runs short double index pointer */ +void vector_ensure(vector v, unsigned int num) +{ + if (v->alloced > num) + return; + + v->index = talloc_realloc_size(tall_vty_vec_ctx, v->index, + sizeof(void *) * (v->alloced * 2)); + memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced); + v->alloced *= 2; + + if (v->alloced <= num) + vector_ensure(v, num); +} + +/* This function only returns next empty slot index. It dose not mean + the slot's index memory is assigned, please call vector_ensure() + after calling this function. */ +int vector_empty_slot(vector v) +{ + unsigned int i; + + if (v->active == 0) + return 0; + + for (i = 0; i < v->active; i++) + if (v->index[i] == 0) + return i; + + return i; +} + +/* Set value to the smallest empty slot. */ +int vector_set(vector v, void *val) +{ + unsigned int i; + + i = vector_empty_slot(v); + vector_ensure(v, i); + + v->index[i] = val; + + if (v->active <= i) + v->active = i + 1; + + return i; +} + +/* Set value to specified index slot. */ +int vector_set_index(vector v, unsigned int i, void *val) +{ + vector_ensure(v, i); + + v->index[i] = val; + + if (v->active <= i) + v->active = i + 1; + + return i; +} + +/* Look up vector. */ +void *vector_lookup(vector v, unsigned int i) +{ + if (i >= v->active) + return NULL; + return v->index[i]; +} + +/* Lookup vector, ensure it. */ +void *vector_lookup_ensure(vector v, unsigned int i) +{ + vector_ensure(v, i); + return v->index[i]; +} + +/* Unset value at specified index slot. */ +void vector_unset(vector v, unsigned int i) +{ + if (i >= v->alloced) + return; + + v->index[i] = NULL; + + if (i + 1 == v->active) { + v->active--; + while (i && v->index[--i] == NULL && v->active--) ; /* Is this ugly ? */ + } +} + +/* Count the number of not emplty slot. */ +unsigned int vector_count(vector v) +{ + unsigned int i; + unsigned count = 0; + + for (i = 0; i < v->active; i++) + if (v->index[i] != NULL) + count++; + + return count; +} diff --git a/src/vty/vty.c b/src/vty/vty.c new file mode 100644 index 00000000..9012b5b5 --- /dev/null +++ b/src/vty/vty.c @@ -0,0 +1,1685 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#define SYSCONFDIR "/usr/local/etc" + +/* our callback, located in telnet_interface.c */ +void vty_event(enum event event, int sock, struct vty *vty); + +extern struct host host; + +/* Vector which store each vty structure. */ +static vector vtyvec; + +vector Vvty_serv_thread; + +char *vty_cwd = NULL; + +/* Configure lock. */ +static int vty_config; + +static int no_password_check = 1; + +void *tall_vty_ctx; + +static void vty_clear_buf(struct vty *vty) +{ + memset(vty->buf, 0, vty->max); +} + +/* Allocate new vty struct. */ +struct vty *vty_new() +{ + struct vty *new = talloc_zero(tall_vty_ctx, struct vty); + + if (!new) + goto out; + + new->obuf = buffer_new(new, 0); /* Use default buffer size. */ + if (!new->obuf) + goto out_new; + new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf"); + if (!new->buf) + goto out_obuf; + + new->max = VTY_BUFSIZ; + + return new; + +out_obuf: + buffer_free(new->obuf); +out_new: + talloc_free(new); + new = NULL; +out: + return new; +} + +/* Authentication of vty */ +static void vty_auth(struct vty *vty, char *buf) +{ + char *passwd = NULL; + enum node_type next_node = 0; + int fail; + char *crypt(const char *, const char *); + + switch (vty->node) { + case AUTH_NODE: +#ifdef VTY_CRYPT_PW + if (host.encrypt) + passwd = host.password_encrypt; + else +#endif + passwd = host.password; + if (host.advanced) + next_node = host.enable ? VIEW_NODE : ENABLE_NODE; + else + next_node = VIEW_NODE; + break; + case AUTH_ENABLE_NODE: +#ifdef VTY_CRYPT_PW + if (host.encrypt) + passwd = host.enable_encrypt; + else +#endif + passwd = host.enable; + next_node = ENABLE_NODE; + break; + } + + if (passwd) { +#ifdef VTY_CRYPT_PW + if (host.encrypt) + fail = strcmp(crypt(buf, passwd), passwd); + else +#endif + fail = strcmp(buf, passwd); + } else + fail = 1; + + if (!fail) { + vty->fail = 0; + vty->node = next_node; /* Success ! */ + } else { + vty->fail++; + if (vty->fail >= 3) { + if (vty->node == AUTH_NODE) { + vty_out(vty, + "%% Bad passwords, too many failures!%s", + VTY_NEWLINE); + vty->status = VTY_CLOSE; + } else { + /* AUTH_ENABLE_NODE */ + vty->fail = 0; + vty_out(vty, + "%% Bad enable passwords, too many failures!%s", + VTY_NEWLINE); + vty->node = VIEW_NODE; + } + } + } +} + +/* Close vty interface. */ +void vty_close(struct vty *vty) +{ + int i; + + if (vty->obuf) { + /* Flush buffer. */ + buffer_flush_all(vty->obuf, vty->fd); + + /* Free input buffer. */ + buffer_free(vty->obuf); + vty->obuf = NULL; + } + + /* Free command history. */ + for (i = 0; i < VTY_MAXHIST; i++) + if (vty->hist[i]) + talloc_free(vty->hist[i]); + + /* Unset vector. */ + vector_unset(vtyvec, vty->fd); + + /* Close socket. */ + if (vty->fd > 0) + close(vty->fd); + + if (vty->buf) { + talloc_free(vty->buf); + vty->buf = NULL; + } + + /* Check configure. */ + vty_config_unlock(vty); + + /* VTY_CLOSED is handled by the telnet_interface */ + vty_event(VTY_CLOSED, vty->fd, vty); + + /* OK free vty. */ + talloc_free(vty); +} + +int vty_shell(struct vty *vty) +{ + return vty->type == VTY_SHELL ? 1 : 0; +} + + +/* VTY standard output function. */ +int vty_out(struct vty *vty, const char *format, ...) +{ + va_list args; + int len = 0; + int size = 1024; + char buf[1024]; + char *p = NULL; + + if (vty_shell(vty)) { + va_start(args, format); + vprintf(format, args); + va_end(args); + } else { + /* Try to write to initial buffer. */ + va_start(args, format); + len = vsnprintf(buf, sizeof buf, format, args); + va_end(args); + + /* Initial buffer is not enough. */ + if (len < 0 || len >= size) { + while (1) { + if (len > -1) + size = len + 1; + else + size = size * 2; + + p = talloc_realloc_size(vty, p, size); + if (!p) + return -1; + + va_start(args, format); + len = vsnprintf(p, size, format, args); + va_end(args); + + if (len > -1 && len < size) + break; + } + } + + /* When initial buffer is enough to store all output. */ + if (!p) + p = buf; + + /* Pointer p must point out buffer. */ + buffer_put(vty->obuf, (u_char *) p, len); + + /* If p is not different with buf, it is allocated buffer. */ + if (p != buf) + talloc_free(p); + } + + vty_event(VTY_WRITE, vty->fd, vty); + + return len; +} + +int vty_out_newline(struct vty *vty) +{ + char *p = vty_newline(vty); + buffer_put(vty->obuf, p, strlen(p)); + return 0; +} + +int vty_config_lock(struct vty *vty) +{ + if (vty_config == 0) { + vty->config = 1; + vty_config = 1; + } + return vty->config; +} + +int vty_config_unlock(struct vty *vty) +{ + if (vty_config == 1 && vty->config == 1) { + vty->config = 0; + vty_config = 0; + } + return vty->config; +} + +/* Say hello to vty interface. */ +void vty_hello(struct vty *vty) +{ + if (host.motdfile) { + FILE *f; + char buf[4096]; + + f = fopen(host.motdfile, "r"); + if (f) { + while (fgets(buf, sizeof(buf), f)) { + char *s; + /* work backwards to ignore trailling isspace() */ + for (s = buf + strlen(buf); + (s > buf) && isspace(*(s - 1)); s--) ; + *s = '\0'; + vty_out(vty, "%s%s", buf, VTY_NEWLINE); + } + fclose(f); + } else + vty_out(vty, "MOTD file not found%s", VTY_NEWLINE); + } else if (host.motd) + vty_out(vty, "%s", host.motd); +} + +/* Put out prompt and wait input from user. */ +static void vty_prompt(struct vty *vty) +{ + struct utsname names; + const char *hostname; + + if (vty->type == VTY_TERM) { + hostname = host.name; + if (!hostname) { + uname(&names); + hostname = names.nodename; + } + vty_out(vty, cmd_prompt(vty->node), hostname); + } +} + +/* Command execution over the vty interface. */ +static int vty_command(struct vty *vty, char *buf) +{ + int ret; + vector vline; + + /* Split readline string up into the vector */ + vline = cmd_make_strvec(buf); + + if (vline == NULL) + return CMD_SUCCESS; + + ret = cmd_execute_command(vline, vty, NULL, 0); + if (ret != CMD_SUCCESS) + switch (ret) { + case CMD_WARNING: + if (vty->type == VTY_FILE) + vty_out(vty, "Warning...%s", VTY_NEWLINE); + break; + case CMD_ERR_AMBIGUOUS: + vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); + break; + case CMD_ERR_NO_MATCH: + vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE); + break; + case CMD_ERR_INCOMPLETE: + vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE); + break; + } + cmd_free_strvec(vline); + + return ret; +} + +static const char telnet_backward_char = 0x08; +static const char telnet_space_char = ' '; + +/* Basic function to write buffer to vty. */ +static void vty_write(struct vty *vty, const char *buf, size_t nbytes) +{ + if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) + return; + + /* Should we do buffering here ? And make vty_flush (vty) ? */ + buffer_put(vty->obuf, buf, nbytes); +} + +/* Ensure length of input buffer. Is buffer is short, double it. */ +static void vty_ensure(struct vty *vty, int length) +{ + if (vty->max <= length) { + vty->max *= 2; + vty->buf = talloc_realloc_size(vty, vty->buf, vty->max); + // FIXME: check return + } +} + +/* Basic function to insert character into vty. */ +static void vty_self_insert(struct vty *vty, char c) +{ + int i; + int length; + + vty_ensure(vty, vty->length + 1); + length = vty->length - vty->cp; + memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length); + vty->buf[vty->cp] = c; + + vty_write(vty, &vty->buf[vty->cp], length + 1); + for (i = 0; i < length; i++) + vty_write(vty, &telnet_backward_char, 1); + + vty->cp++; + vty->length++; +} + +/* Self insert character 'c' in overwrite mode. */ +static void vty_self_insert_overwrite(struct vty *vty, char c) +{ + vty_ensure(vty, vty->length + 1); + vty->buf[vty->cp++] = c; + + if (vty->cp > vty->length) + vty->length++; + + if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) + return; + + vty_write(vty, &c, 1); +} + +/* Insert a word into vty interface with overwrite mode. */ +static void vty_insert_word_overwrite(struct vty *vty, char *str) +{ + int len = strlen(str); + vty_write(vty, str, len); + strcpy(&vty->buf[vty->cp], str); + vty->cp += len; + vty->length = vty->cp; +} + +/* Forward character. */ +static void vty_forward_char(struct vty *vty) +{ + if (vty->cp < vty->length) { + vty_write(vty, &vty->buf[vty->cp], 1); + vty->cp++; + } +} + +/* Backward character. */ +static void vty_backward_char(struct vty *vty) +{ + if (vty->cp > 0) { + vty->cp--; + vty_write(vty, &telnet_backward_char, 1); + } +} + +/* Move to the beginning of the line. */ +static void vty_beginning_of_line(struct vty *vty) +{ + while (vty->cp) + vty_backward_char(vty); +} + +/* Move to the end of the line. */ +static void vty_end_of_line(struct vty *vty) +{ + while (vty->cp < vty->length) + vty_forward_char(vty); +} + +/* Add current command line to the history buffer. */ +static void vty_hist_add(struct vty *vty) +{ + int index; + + if (vty->length == 0) + return; + + index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1; + + /* Ignore the same string as previous one. */ + if (vty->hist[index]) + if (strcmp(vty->buf, vty->hist[index]) == 0) { + vty->hp = vty->hindex; + return; + } + + /* Insert history entry. */ + if (vty->hist[vty->hindex]) + talloc_free(vty->hist[vty->hindex]); + vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf); + + /* History index rotation. */ + vty->hindex++; + if (vty->hindex == VTY_MAXHIST) + vty->hindex = 0; + + vty->hp = vty->hindex; +} + +/* Get telnet window size. */ +static int +vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) +{ +#ifdef TELNET_OPTION_DEBUG + int i; + + for (i = 0; i < nbytes; i++) + { + switch (buf[i]) + { + case IAC: + vty_out (vty, "IAC "); + break; + case WILL: + vty_out (vty, "WILL "); + break; + case WONT: + vty_out (vty, "WONT "); + break; + case DO: + vty_out (vty, "DO "); + break; + case DONT: + vty_out (vty, "DONT "); + break; + case SB: + vty_out (vty, "SB "); + break; + case SE: + vty_out (vty, "SE "); + break; + case TELOPT_ECHO: + vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); + break; + case TELOPT_SGA: + vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); + break; + case TELOPT_NAWS: + vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); + break; + default: + vty_out (vty, "%x ", buf[i]); + break; + } + } + vty_out (vty, "%s", VTY_NEWLINE); + +#endif /* TELNET_OPTION_DEBUG */ + + switch (buf[0]) + { + case SB: + vty->sb_len = 0; + vty->iac_sb_in_progress = 1; + return 0; + break; + case SE: + { + if (!vty->iac_sb_in_progress) + return 0; + + if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) + { + vty->iac_sb_in_progress = 0; + return 0; + } + switch (vty->sb_buf[0]) + { + case TELOPT_NAWS: + if (vty->sb_len != TELNET_NAWS_SB_LEN) + vty_out(vty,"RFC 1073 violation detected: telnet NAWS option " + "should send %d characters, but we received %lu", + TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); + else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) + vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, " + "too small to handle the telnet NAWS option", + (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); + else + { + vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); + vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]); +#ifdef TELNET_OPTION_DEBUG + vty_out(vty, "TELNET NAWS window size negotiation completed: " + "width %d, height %d%s", + vty->width, vty->height, VTY_NEWLINE); +#endif + } + break; + } + vty->iac_sb_in_progress = 0; + return 0; + break; + } + default: + break; + } + return 1; +} + +/* Execute current command line. */ +static int vty_execute(struct vty *vty) +{ + int ret; + + ret = CMD_SUCCESS; + + switch (vty->node) { + case AUTH_NODE: + case AUTH_ENABLE_NODE: + vty_auth(vty, vty->buf); + break; + default: + ret = vty_command(vty, vty->buf); + if (vty->type == VTY_TERM) + vty_hist_add(vty); + break; + } + + /* Clear command line buffer. */ + vty->cp = vty->length = 0; + vty_clear_buf(vty); + + if (vty->status != VTY_CLOSE) + vty_prompt(vty); + + return ret; +} + +/* Send WILL TELOPT_ECHO to remote server. */ +static void +vty_will_echo (struct vty *vty) +{ + unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Make suppress Go-Ahead telnet option. */ +static void +vty_will_suppress_go_ahead (struct vty *vty) +{ + unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Make don't use linemode over telnet. */ +static void +vty_dont_linemode (struct vty *vty) +{ + unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Use window size. */ +static void +vty_do_window_size (struct vty *vty) +{ + unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' }; + vty_out (vty, "%s", cmd); +} + +static void vty_kill_line_from_beginning(struct vty *); +static void vty_redraw_line(struct vty *); + +/* Print command line history. This function is called from + vty_next_line and vty_previous_line. */ +static void vty_history_print(struct vty *vty) +{ + int length; + + vty_kill_line_from_beginning(vty); + + /* Get previous line from history buffer */ + length = strlen(vty->hist[vty->hp]); + memcpy(vty->buf, vty->hist[vty->hp], length); + vty->cp = vty->length = length; + + /* Redraw current line */ + vty_redraw_line(vty); +} + +/* Show next command line history. */ +static void vty_next_line(struct vty *vty) +{ + int try_index; + + if (vty->hp == vty->hindex) + return; + + /* Try is there history exist or not. */ + try_index = vty->hp; + if (try_index == (VTY_MAXHIST - 1)) + try_index = 0; + else + try_index++; + + /* If there is not history return. */ + if (vty->hist[try_index] == NULL) + return; + else + vty->hp = try_index; + + vty_history_print(vty); +} + +/* Show previous command line history. */ +static void vty_previous_line(struct vty *vty) +{ + int try_index; + + try_index = vty->hp; + if (try_index == 0) + try_index = VTY_MAXHIST - 1; + else + try_index--; + + if (vty->hist[try_index] == NULL) + return; + else + vty->hp = try_index; + + vty_history_print(vty); +} + +/* This function redraw all of the command line character. */ +static void vty_redraw_line(struct vty *vty) +{ + vty_write(vty, vty->buf, vty->length); + vty->cp = vty->length; +} + +/* Forward word. */ +static void vty_forward_word(struct vty *vty) +{ + while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') + vty_forward_char(vty); + + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') + vty_forward_char(vty); +} + +/* Backward word without skipping training space. */ +static void vty_backward_pure_word(struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_backward_char(vty); +} + +/* Backward word. */ +static void vty_backward_word(struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') + vty_backward_char(vty); + + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_backward_char(vty); +} + +/* When '^D' is typed at the beginning of the line we move to the down + level. */ +static void vty_down_level(struct vty *vty) +{ + vty_out(vty, "%s", VTY_NEWLINE); + /* FIXME: we need to call the exit function of the specific node + * in question, not this generic one that doesn't know all nodes */ + (*config_exit_cmd.func) (NULL, vty, 0, NULL); + vty_prompt(vty); + vty->cp = 0; +} + +/* When '^Z' is received from vty, move down to the enable mode. */ +static void vty_end_config(struct vty *vty) +{ + vty_out(vty, "%s", VTY_NEWLINE); + + /* FIXME: we need to call the exit function of the specific node + * in question, not this generic one that doesn't know all nodes */ + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case VTY_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + break; + default: + /* Unknown node, we have to ignore it. */ + break; + } + + vty_prompt(vty); + vty->cp = 0; +} + +/* Delete a charcter at the current point. */ +static void vty_delete_char(struct vty *vty) +{ + int i; + int size; + + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + return; + + if (vty->length == 0) { + vty_down_level(vty); + return; + } + + if (vty->cp == vty->length) + return; /* completion need here? */ + + size = vty->length - vty->cp; + + vty->length--; + memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1); + vty->buf[vty->length] = '\0'; + + vty_write(vty, &vty->buf[vty->cp], size - 1); + vty_write(vty, &telnet_space_char, 1); + + for (i = 0; i < size; i++) + vty_write(vty, &telnet_backward_char, 1); +} + +/* Delete a character before the point. */ +static void vty_delete_backward_char(struct vty *vty) +{ + if (vty->cp == 0) + return; + + vty_backward_char(vty); + vty_delete_char(vty); +} + +/* Kill rest of line from current point. */ +static void vty_kill_line(struct vty *vty) +{ + int i; + int size; + + size = vty->length - vty->cp; + + if (size == 0) + return; + + for (i = 0; i < size; i++) + vty_write(vty, &telnet_space_char, 1); + for (i = 0; i < size; i++) + vty_write(vty, &telnet_backward_char, 1); + + memset(&vty->buf[vty->cp], 0, size); + vty->length = vty->cp; +} + +/* Kill line from the beginning. */ +static void vty_kill_line_from_beginning(struct vty *vty) +{ + vty_beginning_of_line(vty); + vty_kill_line(vty); +} + +/* Delete a word before the point. */ +static void vty_forward_kill_word(struct vty *vty) +{ + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') + vty_delete_char(vty); + while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') + vty_delete_char(vty); +} + +/* Delete a word before the point. */ +static void vty_backward_kill_word(struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') + vty_delete_backward_char(vty); + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_delete_backward_char(vty); +} + +/* Transpose chars before or at the point. */ +static void vty_transpose_chars(struct vty *vty) +{ + char c1, c2; + + /* If length is short or point is near by the beginning of line then + return. */ + if (vty->length < 2 || vty->cp < 1) + return; + + /* In case of point is located at the end of the line. */ + if (vty->cp == vty->length) { + c1 = vty->buf[vty->cp - 1]; + c2 = vty->buf[vty->cp - 2]; + + vty_backward_char(vty); + vty_backward_char(vty); + vty_self_insert_overwrite(vty, c1); + vty_self_insert_overwrite(vty, c2); + } else { + c1 = vty->buf[vty->cp]; + c2 = vty->buf[vty->cp - 1]; + + vty_backward_char(vty); + vty_self_insert_overwrite(vty, c1); + vty_self_insert_overwrite(vty, c2); + } +} + +/* Do completion at vty interface. */ +static void vty_complete_command(struct vty *vty) +{ + int i; + int ret; + char **matched = NULL; + vector vline; + + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + return; + + vline = cmd_make_strvec(vty->buf); + if (vline == NULL) + return; + + /* In case of 'help \t'. */ + if (isspace((int)vty->buf[vty->length - 1])) + vector_set(vline, '\0'); + + matched = cmd_complete_command(vline, vty, &ret); + + cmd_free_strvec(vline); + + vty_out(vty, "%s", VTY_NEWLINE); + switch (ret) { + case CMD_ERR_AMBIGUOUS: + vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); + vty_prompt(vty); + vty_redraw_line(vty); + break; + case CMD_ERR_NO_MATCH: + /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */ + vty_prompt(vty); + vty_redraw_line(vty); + break; + case CMD_COMPLETE_FULL_MATCH: + vty_prompt(vty); + vty_redraw_line(vty); + vty_backward_pure_word(vty); + vty_insert_word_overwrite(vty, matched[0]); + vty_self_insert(vty, ' '); + talloc_free(matched[0]); + break; + case CMD_COMPLETE_MATCH: + vty_prompt(vty); + vty_redraw_line(vty); + vty_backward_pure_word(vty); + vty_insert_word_overwrite(vty, matched[0]); + talloc_free(matched[0]); + break; + case CMD_COMPLETE_LIST_MATCH: + for (i = 0; matched[i] != NULL; i++) { + if (i != 0 && ((i % 6) == 0)) + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%-10s ", matched[i]); + talloc_free(matched[i]); + } + vty_out(vty, "%s", VTY_NEWLINE); + + vty_prompt(vty); + vty_redraw_line(vty); + break; + case CMD_ERR_NOTHING_TODO: + vty_prompt(vty); + vty_redraw_line(vty); + break; + default: + break; + } + if (matched) + vector_only_index_free(matched); +} + +static void +vty_describe_fold(struct vty *vty, int cmd_width, + unsigned int desc_width, struct desc *desc) +{ + char *buf; + const char *cmd, *p; + int pos; + + cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; + + if (desc_width <= 0) { + vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str, + VTY_NEWLINE); + return; + } + + buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold"); + if (!buf) + return; + + for (p = desc->str; strlen(p) > desc_width; p += pos + 1) { + for (pos = desc_width; pos > 0; pos--) + if (*(p + pos) == ' ') + break; + + if (pos == 0) + break; + + strncpy(buf, p, pos); + buf[pos] = '\0'; + vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE); + + cmd = ""; + } + + vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE); + + talloc_free(buf); +} + +/* Describe matched command function. */ +static void vty_describe_command(struct vty *vty) +{ + int ret; + vector vline; + vector describe; + unsigned int i, width, desc_width; + struct desc *desc, *desc_cr = NULL; + + vline = cmd_make_strvec(vty->buf); + + /* In case of '> ?'. */ + if (vline == NULL) { + vline = vector_init(1); + vector_set(vline, '\0'); + } else if (isspace((int)vty->buf[vty->length - 1])) + vector_set(vline, '\0'); + + describe = cmd_describe_command(vline, vty, &ret); + + vty_out(vty, "%s", VTY_NEWLINE); + + /* Ambiguous error. */ + switch (ret) { + case CMD_ERR_AMBIGUOUS: + cmd_free_strvec(vline); + vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); + vty_prompt(vty); + vty_redraw_line(vty); + return; + break; + case CMD_ERR_NO_MATCH: + cmd_free_strvec(vline); + vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE); + vty_prompt(vty); + vty_redraw_line(vty); + return; + break; + } + + /* Get width of command string. */ + width = 0; + for (i = 0; i < vector_active(describe); i++) + if ((desc = vector_slot(describe, i)) != NULL) { + unsigned int len; + + if (desc->cmd[0] == '\0') + continue; + + len = strlen(desc->cmd); + if (desc->cmd[0] == '.') + len--; + + if (width < len) + width = len; + } + + /* Get width of description string. */ + desc_width = vty->width - (width + 6); + + /* Print out description. */ + for (i = 0; i < vector_active(describe); i++) + if ((desc = vector_slot(describe, i)) != NULL) { + if (desc->cmd[0] == '\0') + continue; + + if (strcmp(desc->cmd, "") == 0) { + desc_cr = desc; + continue; + } + + if (!desc->str) + vty_out(vty, " %-s%s", + desc->cmd[0] == + '.' ? desc->cmd + 1 : desc->cmd, + VTY_NEWLINE); + else if (desc_width >= strlen(desc->str)) + vty_out(vty, " %-*s %s%s", width, + desc->cmd[0] == + '.' ? desc->cmd + 1 : desc->cmd, + desc->str, VTY_NEWLINE); + else + vty_describe_fold(vty, width, desc_width, desc); + +#if 0 + vty_out(vty, " %-*s %s%s", width + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + desc->str ? desc->str : "", VTY_NEWLINE); +#endif /* 0 */ + } + + if ((desc = desc_cr)) { + if (!desc->str) + vty_out(vty, " %-s%s", + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + VTY_NEWLINE); + else if (desc_width >= strlen(desc->str)) + vty_out(vty, " %-*s %s%s", width, + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + desc->str, VTY_NEWLINE); + else + vty_describe_fold(vty, width, desc_width, desc); + } + + cmd_free_strvec(vline); + vector_free(describe); + + vty_prompt(vty); + vty_redraw_line(vty); +} + +/* ^C stop current input and do not add command line to the history. */ +static void vty_stop_input(struct vty *vty) +{ + vty->cp = vty->length = 0; + vty_clear_buf(vty); + vty_out(vty, "%s", VTY_NEWLINE); + + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case VTY_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + break; + default: + /* Unknown node, we have to ignore it. */ + break; + } + vty_prompt(vty); + + /* Set history pointer to the latest one. */ + vty->hp = vty->hindex; +} + +#define CONTROL(X) ((X) - '@') +#define VTY_NORMAL 0 +#define VTY_PRE_ESCAPE 1 +#define VTY_ESCAPE 2 + +/* Escape character command map. */ +static void vty_escape_map(unsigned char c, struct vty *vty) +{ + switch (c) { + case ('A'): + vty_previous_line(vty); + break; + case ('B'): + vty_next_line(vty); + break; + case ('C'): + vty_forward_char(vty); + break; + case ('D'): + vty_backward_char(vty); + break; + default: + break; + } + + /* Go back to normal mode. */ + vty->escape = VTY_NORMAL; +} + +/* Quit print out to the buffer. */ +static void vty_buffer_reset(struct vty *vty) +{ + buffer_reset(vty->obuf); + vty_prompt(vty); + vty_redraw_line(vty); +} + +/* Read data via vty socket. */ +int vty_read(struct vty *vty) +{ + int i; + int nbytes; + unsigned char buf[VTY_READ_BUFSIZ]; + + int vty_sock = vty->fd; + + /* Read raw data from socket */ + if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) { + if (nbytes < 0) { + if (ERRNO_IO_RETRY(errno)) { + vty_event(VTY_READ, vty_sock, vty); + return 0; + } + } + buffer_reset(vty->obuf); + vty->status = VTY_CLOSE; + } + + for (i = 0; i < nbytes; i++) { + if (buf[i] == IAC) { + if (!vty->iac) { + vty->iac = 1; + continue; + } else { + vty->iac = 0; + } + } + + if (vty->iac_sb_in_progress && !vty->iac) { + if (vty->sb_len < sizeof(vty->sb_buf)) + vty->sb_buf[vty->sb_len] = buf[i]; + vty->sb_len++; + continue; + } + + if (vty->iac) { + /* In case of telnet command */ + int ret = 0; + ret = vty_telnet_option(vty, buf + i, nbytes - i); + vty->iac = 0; + i += ret; + continue; + } + + if (vty->status == VTY_MORE) { + switch (buf[i]) { + case CONTROL('C'): + case 'q': + case 'Q': + vty_buffer_reset(vty); + break; +#if 0 /* More line does not work for "show ip bgp". */ + case '\n': + case '\r': + vty->status = VTY_MORELINE; + break; +#endif + default: + break; + } + continue; + } + + /* Escape character. */ + if (vty->escape == VTY_ESCAPE) { + vty_escape_map(buf[i], vty); + continue; + } + + /* Pre-escape status. */ + if (vty->escape == VTY_PRE_ESCAPE) { + switch (buf[i]) { + case '[': + vty->escape = VTY_ESCAPE; + break; + case 'b': + vty_backward_word(vty); + vty->escape = VTY_NORMAL; + break; + case 'f': + vty_forward_word(vty); + vty->escape = VTY_NORMAL; + break; + case 'd': + vty_forward_kill_word(vty); + vty->escape = VTY_NORMAL; + break; + case CONTROL('H'): + case 0x7f: + vty_backward_kill_word(vty); + vty->escape = VTY_NORMAL; + break; + default: + vty->escape = VTY_NORMAL; + break; + } + continue; + } + + switch (buf[i]) { + case CONTROL('A'): + vty_beginning_of_line(vty); + break; + case CONTROL('B'): + vty_backward_char(vty); + break; + case CONTROL('C'): + vty_stop_input(vty); + break; + case CONTROL('D'): + vty_delete_char(vty); + break; + case CONTROL('E'): + vty_end_of_line(vty); + break; + case CONTROL('F'): + vty_forward_char(vty); + break; + case CONTROL('H'): + case 0x7f: + vty_delete_backward_char(vty); + break; + case CONTROL('K'): + vty_kill_line(vty); + break; + case CONTROL('N'): + vty_next_line(vty); + break; + case CONTROL('P'): + vty_previous_line(vty); + break; + case CONTROL('T'): + vty_transpose_chars(vty); + break; + case CONTROL('U'): + vty_kill_line_from_beginning(vty); + break; + case CONTROL('W'): + vty_backward_kill_word(vty); + break; + case CONTROL('Z'): + vty_end_config(vty); + break; + case '\n': + case '\r': + vty_out(vty, "%s", VTY_NEWLINE); + vty_execute(vty); + break; + case '\t': + vty_complete_command(vty); + break; + case '?': + if (vty->node == AUTH_NODE + || vty->node == AUTH_ENABLE_NODE) + vty_self_insert(vty, buf[i]); + else + vty_describe_command(vty); + break; + case '\033': + if (i + 1 < nbytes && buf[i + 1] == '[') { + vty->escape = VTY_ESCAPE; + i++; + } else + vty->escape = VTY_PRE_ESCAPE; + break; + default: + if (buf[i] > 31 && buf[i] < 127) + vty_self_insert(vty, buf[i]); + break; + } + } + + /* Check status. */ + if (vty->status == VTY_CLOSE) + vty_close(vty); + else { + vty_event(VTY_WRITE, vty_sock, vty); + vty_event(VTY_READ, vty_sock, vty); + } + return 0; +} + +/* Read up configuration file */ +static int +vty_read_file(FILE *confp, void *priv) +{ + int ret; + struct vty *vty; + + vty = vty_new(); + vty->fd = 0; + vty->type = VTY_FILE; + vty->node = CONFIG_NODE; + vty->priv = priv; + + ret = config_from_file(vty, confp); + + if (ret != CMD_SUCCESS) { + switch (ret) { + case CMD_ERR_AMBIGUOUS: + fprintf(stderr, "Ambiguous command.\n"); + break; + case CMD_ERR_NO_MATCH: + fprintf(stderr, "There is no such command.\n"); + break; + } + fprintf(stderr, "Error occurred during reading below " + "line:\n%s\n", vty->buf); + vty_close(vty); + return -EINVAL; + } + + vty_close(vty); + return 0; +} + +/* Create new vty structure. */ +struct vty * +vty_create (int vty_sock, void *priv) +{ + struct vty *vty; + + struct termios t; + + tcgetattr(vty_sock, &t); + cfmakeraw(&t); + tcsetattr(vty_sock, TCSANOW, &t); + + /* Allocate new vty structure and set up default values. */ + vty = vty_new (); + vty->fd = vty_sock; + vty->priv = priv; + vty->type = VTY_TERM; + if (no_password_check) + { + if (host.advanced) + vty->node = ENABLE_NODE; + else + vty->node = VIEW_NODE; + } + else + vty->node = AUTH_NODE; + vty->fail = 0; + vty->cp = 0; + vty_clear_buf (vty); + vty->length = 0; + memset (vty->hist, 0, sizeof (vty->hist)); + vty->hp = 0; + vty->hindex = 0; + vector_set_index (vtyvec, vty_sock, vty); + vty->status = VTY_NORMAL; + if (host.lines >= 0) + vty->lines = host.lines; + else + vty->lines = -1; + + if (! no_password_check) + { + /* Vty is not available if password isn't set. */ + if (host.password == NULL && host.password_encrypt == NULL) + { + vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); + vty->status = VTY_CLOSE; + vty_close (vty); + return NULL; + } + } + + /* Say hello to the world. */ + vty_hello (vty); + if (! no_password_check) + vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + /* Setting up terminal. */ + vty_will_echo (vty); + vty_will_suppress_go_ahead (vty); + + vty_dont_linemode (vty); + vty_do_window_size (vty); + /* vty_dont_lflow_ahead (vty); */ + + vty_prompt (vty); + + /* Add read/write thread. */ + vty_event (VTY_WRITE, vty_sock, vty); + vty_event (VTY_READ, vty_sock, vty); + + return vty; +} + +DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n") +{ + unsigned int i; + struct vty *v; + + for (i = 0; i < vector_active(vtyvec); i++) + if ((v = vector_slot(vtyvec, i)) != NULL) + vty_out(vty, "%svty[%d] %s", + v->config ? "*" : " ", i, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Move to vty configuration mode. */ +DEFUN(line_vty, + line_vty_cmd, + "line vty", "Configure a terminal line\n" "Virtual terminal\n") +{ + vty->node = VTY_NODE; + return CMD_SUCCESS; +} + +/* vty login. */ +DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n") +{ + no_password_check = 0; + return CMD_SUCCESS; +} + +DEFUN(no_vty_login, + no_vty_login_cmd, "no login", NO_STR "Enable password checking\n") +{ + no_password_check = 1; + return CMD_SUCCESS; +} + +DEFUN(service_advanced_vty, + service_advanced_vty_cmd, + "service advanced-vty", + "Set up miscellaneous service\n" "Enable advanced mode vty interface\n") +{ + host.advanced = 1; + return CMD_SUCCESS; +} + +DEFUN(no_service_advanced_vty, + no_service_advanced_vty_cmd, + "no service advanced-vty", + NO_STR + "Set up miscellaneous service\n" "Enable advanced mode vty interface\n") +{ + host.advanced = 0; + return CMD_SUCCESS; +} + +DEFUN(terminal_monitor, + terminal_monitor_cmd, + "terminal monitor", + "Set terminal line parameters\n" + "Copy debug output to the current terminal line\n") +{ + vty->monitor = 1; + return CMD_SUCCESS; +} + +DEFUN(terminal_no_monitor, + terminal_no_monitor_cmd, + "terminal no monitor", + "Set terminal line parameters\n" + NO_STR "Copy debug output to the current terminal line\n") +{ + vty->monitor = 0; + return CMD_SUCCESS; +} + +DEFUN(show_history, + show_history_cmd, + "show history", SHOW_STR "Display the session command history\n") +{ + int index; + + for (index = vty->hindex + 1; index != vty->hindex;) { + if (index == VTY_MAXHIST) { + index = 0; + continue; + } + + if (vty->hist[index] != NULL) + vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE); + + index++; + } + + return CMD_SUCCESS; +} + +/* Display current configuration. */ +static int vty_config_write(struct vty *vty) +{ + vty_out(vty, "line vty%s", VTY_NEWLINE); + + /* login */ + if (no_password_check) + vty_out(vty, " no login%s", VTY_NEWLINE); + + vty_out(vty, "!%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +struct cmd_node vty_node = { + VTY_NODE, + "%s(config-line)# ", + 1, +}; + +/* Reset all VTY status. */ +void vty_reset() +{ + unsigned int i; + struct vty *vty; + struct thread *vty_serv_thread; + + for (i = 0; i < vector_active(vtyvec); i++) + if ((vty = vector_slot(vtyvec, i)) != NULL) { + buffer_reset(vty->obuf); + vty->status = VTY_CLOSE; + vty_close(vty); + } + + for (i = 0; i < vector_active(Vvty_serv_thread); i++) + if ((vty_serv_thread = + vector_slot(Vvty_serv_thread, i)) != NULL) { + //thread_cancel (vty_serv_thread); + vector_slot(Vvty_serv_thread, i) = NULL; + close(i); + } +} + +static void vty_save_cwd(void) +{ + char cwd[MAXPATHLEN]; + char *c ; + + c = getcwd(cwd, MAXPATHLEN); + + if (!c) { + if (chdir(SYSCONFDIR) != 0) + perror("chdir failed"); + if (getcwd(cwd, MAXPATHLEN) == NULL) + perror("getcwd failed"); + } + + vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd"); + strcpy(vty_cwd, cwd); +} + +char *vty_get_cwd() +{ + return vty_cwd; +} + +int vty_shell_serv(struct vty *vty) +{ + return vty->type == VTY_SHELL_SERV ? 1 : 0; +} + +void vty_init_vtysh() +{ + vtyvec = vector_init(VECTOR_MIN_SIZE); +} + +extern void *tall_bsc_ctx; +/* Install vty's own commands like `who' command. */ +void vty_init(const char *name, const char *version, const char *copyright) +{ + tall_vty_ctx = talloc_named_const(NULL, 0, "vty"); + tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector"); + tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command"); + + cmd_init(1); + + host.prog_name = name; + host.prog_version = version; + host.prog_copyright = copyright; + + /* For further configuration read, preserve current directory. */ + vty_save_cwd(); + + vtyvec = vector_init(VECTOR_MIN_SIZE); + + /* Install bgp top node. */ + install_node(&vty_node, vty_config_write); + + install_element_ve(&config_who_cmd); + install_element_ve(&show_history_cmd); + install_element(CONFIG_NODE, &line_vty_cmd); + install_element(CONFIG_NODE, &service_advanced_vty_cmd); + install_element(CONFIG_NODE, &no_service_advanced_vty_cmd); + install_element(CONFIG_NODE, &show_history_cmd); + install_element(ENABLE_NODE, &terminal_monitor_cmd); + install_element(ENABLE_NODE, &terminal_no_monitor_cmd); + + install_default(VTY_NODE); + install_element(VTY_NODE, &vty_login_cmd); + install_element(VTY_NODE, &no_vty_login_cmd); +} + +int vty_read_config_file(const char *file_name, void *priv) +{ + FILE *cfile; + int rc; + + cfile = fopen(file_name, "r"); + if (!cfile) + return -ENOENT; + + rc = vty_read_file(cfile, priv); + fclose(cfile); + + host_config_set(file_name); + + return rc; +} -- cgit v1.2.3 From ab1b819930fc891d2efb75651e781b0b60ea366c Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 19 May 2010 19:25:40 +0200 Subject: make sure we actually install the vty headers --- include/osmocom/vty/Makefile.am | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/osmocom/vty/Makefile.am b/include/osmocom/vty/Makefile.am index e51dcb83..d2f0616d 100644 --- a/include/osmocom/vty/Makefile.am +++ b/include/osmocom/vty/Makefile.am @@ -1,2 +1,4 @@ -noinst_HEADERS = buffer.h command.h vector.h vty.h \ - telnet_interface.h +osmovty_HEADERS = buffer.h command.h vector.h vty.h \ + telnet_interface.h logging.h + +osmovtydir = $(includedir)/osmocom/vty -- cgit v1.2.3 From e352c52ed8f60276cf6bfe71abf3f1c6c06c2b8d Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 19 May 2010 19:42:32 +0200 Subject: Add pkgconfig for libosmovty --- Makefile.am | 2 +- configure.in | 1 + libosmovty.pc.in | 11 +++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 libosmovty.pc.in diff --git a/Makefile.am b/Makefile.am index 2adf5023..81da6294 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,7 +5,7 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include SUBDIRS = include src tests pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = libosmocore.pc +pkgconfig_DATA = libosmocore.pc libosmovty.pc BUILT_SOURCES = $(top_srcdir)/.version $(top_srcdir)/.version: diff --git a/configure.in b/configure.in index abf098cc..6e427733 100644 --- a/configure.in +++ b/configure.in @@ -46,6 +46,7 @@ AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "x1") AC_OUTPUT( libosmocore.pc + libosmovty.pc include/osmocom/Makefile include/osmocom/vty/Makefile include/osmocore/Makefile diff --git a/libosmovty.pc.in b/libosmovty.pc.in new file mode 100644 index 00000000..2cc0b5f8 --- /dev/null +++ b/libosmovty.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Osmocom VTY Interface Library +Description: C Utility Library +Version: @VERSION@ +Libs: -L${libdir} -losmovty +Cflags: -I${includedir}/ + -- cgit v1.2.3 From 93cf5a38eb503128016ce57f71071ca2f4415650 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 19 May 2010 19:44:57 +0200 Subject: fix typo resultin in dist/distcheck problem --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.am b/src/Makefile.am index 2e2eaf0e..1a7d87f3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -SUBDRIS=vty +SUBDIRS=vty # This is _NOT_ the library release version, it's an API version. # Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification -- cgit v1.2.3 From 4ebdf747286541c52311517fbedd0cb128631a61 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 19 May 2010 19:54:00 +0200 Subject: rename log_info to osmo_log_info to avoid namespace clash with app --- src/logging.c | 30 +++++++++++++++--------------- src/vty/logging_vty.c | 4 ++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/logging.c b/src/logging.c index 531cea6c..1dc30db3 100644 --- a/src/logging.c +++ b/src/logging.c @@ -37,7 +37,7 @@ #include #include -const struct log_info *log_info; +const struct log_info *osmo_log_info; static struct log_context log_context; static void *tall_log_ctx = NULL; @@ -67,8 +67,8 @@ int log_parse_category(const char *category) { int i; - for (i = 0; i < log_info->num_cat; ++i) { - if (!strcasecmp(log_info->cat[i].name+1, category)) + for (i = 0; i < osmo_log_info->num_cat; ++i) { + if (!strcasecmp(osmo_log_info->cat[i].name+1, category)) return i; } @@ -92,15 +92,15 @@ void log_parse_category_mask(struct log_target* target, const char *_mask) category_token = strtok(mask, ":"); do { - for (i = 0; i < log_info->num_cat; ++i) { + for (i = 0; i < osmo_log_info->num_cat; ++i) { char* colon = strstr(category_token, ","); int length = strlen(category_token); if (colon) length = colon - category_token; - if (strncasecmp(log_info->cat[i].name, category_token, - length) == 0) { + if (strncasecmp(osmo_log_info->cat[i].name, + category_token, length) == 0) { int level = 0; if (colon) @@ -117,8 +117,8 @@ void log_parse_category_mask(struct log_target* target, const char *_mask) static const char* color(int subsys) { - if (subsys < log_info->num_cat) - return log_info->cat[subsys].color; + if (subsys < osmo_log_info->num_cat) + return osmo_log_info->cat[subsys].color; return NULL; } @@ -198,8 +198,8 @@ static void _logp(unsigned int subsys, int level, char *file, int line, * say stop, continue, output */ if ((tar->filter_map & LOG_FILTER_ALL) != 0) output = 1; - else if (log_info->filter_fn) - output = log_info->filter_fn(&log_context, + else if (osmo_log_info->filter_fn) + output = osmo_log_info->filter_fn(&log_context, tar); if (output) { @@ -306,7 +306,7 @@ void log_set_log_level(struct log_target *target, int log_level) void log_set_category_filter(struct log_target *target, int category, int enable, int level) { - if (category >= log_info->num_cat) + if (category >= osmo_log_info->num_cat) return; target->categories[category].enabled = !!enable; target->categories[category].loglevel = level; @@ -333,10 +333,10 @@ struct log_target *log_target_create(void) INIT_LLIST_HEAD(&target->entry); /* initialize the per-category enabled/loglevel from defaults */ - for (i = 0; i < log_info->num_cat; i++) { + for (i = 0; i < osmo_log_info->num_cat; i++) { struct log_category *cat = &target->categories[i]; - cat->enabled = log_info->cat[i].enabled; - cat->loglevel = log_info->cat[i].loglevel; + cat->enabled = osmo_log_info->cat[i].enabled; + cat->loglevel = osmo_log_info->cat[i].loglevel; } /* global settings */ @@ -415,5 +415,5 @@ const char *log_vty_category_string(struct log_info *info) void log_init(const struct log_info *cat) { tall_log_ctx = talloc_named_const(NULL, 1, "logging"); - log_info = cat; + osmo_log_info = cat; } diff --git a/src/vty/logging_vty.c b/src/vty/logging_vty.c index a7871963..896d79a9 100644 --- a/src/vty/logging_vty.c +++ b/src/vty/logging_vty.c @@ -33,7 +33,7 @@ #include #include -extern const struct log_info *log_info; +extern const struct log_info *osmo_log_info; static void _vty_output(struct log_target *tgt, const char *line) { @@ -291,7 +291,7 @@ DEFUN(show_logging_vty, vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); return CMD_WARNING; } - vty_print_logtarget(vty, log_info, conn->dbg); + vty_print_logtarget(vty, osmo_log_info, conn->dbg); return CMD_SUCCESS; } -- cgit v1.2.3 From 237f6241f2b91a81b928ce4e3fc1364f61f11eaa Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 25 May 2010 23:00:45 +0200 Subject: [VTY] Introduce "struct vty_app_info" for vty_init() function --- include/osmocom/vty/command.h | 6 +----- include/osmocom/vty/vty.h | 10 +++++++++- src/vty/command.c | 17 +++++++++-------- src/vty/vty.c | 8 +++----- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/include/osmocom/vty/command.h b/include/osmocom/vty/command.h index 1db8ffea..0136f576 100644 --- a/include/osmocom/vty/command.h +++ b/include/osmocom/vty/command.h @@ -58,9 +58,7 @@ struct host { const char *motd; char *motdfile; - const char *prog_name; - const char *prog_version; - const char *prog_copyright; + const struct vty_app_info *app_info; }; /* There are some command levels which called from command node. */ @@ -387,6 +385,4 @@ void print_version(int print_copyright); extern void *tall_vty_cmd_ctx; -enum node_type (*vty_go_parent_cb)(struct vty *vty); - #endif /* _ZEBRA_COMMAND_H */ diff --git a/include/osmocom/vty/vty.h b/include/osmocom/vty/vty.h index f82ada1c..f0174624 100644 --- a/include/osmocom/vty/vty.h +++ b/include/osmocom/vty/vty.h @@ -127,8 +127,16 @@ static inline char *vty_newline(struct vty *vty) return VTY_NEWLINE; } +struct vty_app_info { + char *name; + char *version; + char *copyright; + void *tall_ctx; + enum node_type (*go_parent_cb)(struct vty *vty); +}; + /* Prototypes. */ -void vty_init(const char *name, const char *version, const char *copyright); +void vty_init(struct vty_app_info *app_info); int vty_read_config_file(const char *file_name, void *priv); void vty_init_vtysh (void); void vty_reset (void); diff --git a/src/vty/command.c b/src/vty/command.c index 7275a3d4..21afa5c0 100644 --- a/src/vty/command.c +++ b/src/vty/command.c @@ -84,9 +84,9 @@ const char *default_motd = ""; /* This is called from main when a daemon is invoked with -v or --version. */ void print_version(int print_copyright) { - printf("%s version %s\n", host.prog_name, host.prog_version); + printf("%s version %s\n", host.app_info->name, host.app_info->version); if (print_copyright) - printf("\n%s\n", host.prog_copyright); + printf("\n%s\n", host.app_info->copyright); } /* Utility function to concatenate argv argument into a single string @@ -1751,8 +1751,8 @@ enum node_type vty_go_parent(struct vty *vty) { assert(vty->node > CONFIG_NODE); - if (vty_go_parent_cb) - vty_go_parent_cb(vty); + if (host.app_info->go_parent_cb) + host.app_info->go_parent_cb(vty); else vty->node = CONFIG_NODE; @@ -2162,9 +2162,10 @@ gDEFUN(config_exit, DEFUN(show_version, show_version_cmd, "show version", SHOW_STR "Displays program version\n") { - vty_out(vty, "%s %s (%s).%s", host.prog_name, host.prog_version, - host.name ? host.name : "", VTY_NEWLINE); - vty_out(vty, "%s%s", host.prog_copyright, VTY_NEWLINE); + vty_out(vty, "%s %s (%s).%s", host.app_info->name, + host.app_info->version, + host.app_info->name ? host.app_info->name : "", VTY_NEWLINE); + vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE); return CMD_SUCCESS; } @@ -2258,7 +2259,7 @@ DEFUN(config_write_file, /* Config file header print. */ vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!", - host.prog_name, host.prog_version); + host.app_info->name, host.app_info->version); //vty_time_print (file_vty, 1); vty_out(file_vty, "!\n"); diff --git a/src/vty/vty.c b/src/vty/vty.c index 9012b5b5..ff17abf6 100644 --- a/src/vty/vty.c +++ b/src/vty/vty.c @@ -1633,17 +1633,15 @@ void vty_init_vtysh() extern void *tall_bsc_ctx; /* Install vty's own commands like `who' command. */ -void vty_init(const char *name, const char *version, const char *copyright) +void vty_init(struct vty_app_info *app_info) { - tall_vty_ctx = talloc_named_const(NULL, 0, "vty"); + tall_vty_ctx = talloc_named_const(app_info->tall_ctx, 0, "vty"); tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector"); tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command"); cmd_init(1); - host.prog_name = name; - host.prog_version = version; - host.prog_copyright = copyright; + host.app_info = app_info; /* For further configuration read, preserve current directory. */ vty_save_cwd(); -- cgit v1.2.3 From 544037700c01bf557f4db88dd1cbe8ab7a4f7f8c Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 29 May 2010 11:49:51 +0200 Subject: add configure option '--disable-vty' to prevent building libosmovty --- configure.in | 6 ++++++ include/osmocom/Makefile.am | 2 ++ src/vty/Makefile.am | 2 ++ 3 files changed, 10 insertions(+) diff --git a/configure.in b/configure.in index 6e427733..cc8fdf61 100644 --- a/configure.in +++ b/configure.in @@ -44,6 +44,12 @@ AC_ARG_ENABLE(tests, [enable_tests=0], [enable_tests=1]) AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "x1") +AC_ARG_ENABLE(vtyc, + [ --disable-vty Disable building VTY telnet interface ], + [enable_vty=0], [enable_vty=1]) +AM_CONDITIONAL(ENABLE_VTY, test "x$enable_vty" = "x1") + + AC_OUTPUT( libosmocore.pc libosmovty.pc diff --git a/include/osmocom/Makefile.am b/include/osmocom/Makefile.am index c9023e65..71293d15 100644 --- a/include/osmocom/Makefile.am +++ b/include/osmocom/Makefile.am @@ -1 +1,3 @@ +if ENABLE_VTY SUBDIRS = vty +endif diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am index bc10a0f8..f2859cff 100644 --- a/src/vty/Makefile.am +++ b/src/vty/Makefile.am @@ -5,7 +5,9 @@ LIBVERSION=0:0:0 INCLUDES = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS = -fPIC -Wall +if ENABLE_VTY lib_LTLIBRARIES = libosmovty.la libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \ telnet_interface.c logging_vty.c +endif -- cgit v1.2.3 From 4c053013c9ffe186996668a36ebe7c326083dc58 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 31 May 2010 16:01:59 +0200 Subject: [VTY] Remove all application specific VTY node definitions from enum --- include/osmocom/vty/command.h | 43 ++----------------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/include/osmocom/vty/command.h b/include/osmocom/vty/command.h index 0136f576..69e9e772 100644 --- a/include/osmocom/vty/command.h +++ b/include/osmocom/vty/command.h @@ -70,49 +70,10 @@ enum node_type { CONFIG_NODE, /* Config node. Default mode of config file. */ SERVICE_NODE, /* Service node. */ DEBUG_NODE, /* Debug node. */ -#if 0 - AAA_NODE, /* AAA node. */ - KEYCHAIN_NODE, /* Key-chain node. */ - KEYCHAIN_KEY_NODE, /* Key-chain key node. */ - INTERFACE_NODE, /* Interface mode node. */ - ZEBRA_NODE, /* zebra connection node. */ - TABLE_NODE, /* rtm_table selection node. */ - RIP_NODE, /* RIP protocol mode node. */ - RIPNG_NODE, /* RIPng protocol mode node. */ - BGP_NODE, /* BGP protocol mode which includes BGP4+ */ - BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ - BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ - BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ - BGP_IPV6_NODE, /* BGP IPv6 address family */ - OSPF_NODE, /* OSPF protocol mode */ - OSPF6_NODE, /* OSPF protocol for IPv6 mode */ - ISIS_NODE, /* ISIS protocol mode */ - MASC_NODE, /* MASC for multicast. */ - IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ - IP_NODE, /* Static ip route node. */ - ACCESS_NODE, /* Access list node. */ - PREFIX_NODE, /* Prefix list node. */ - ACCESS_IPV6_NODE, /* Access list node. */ - PREFIX_IPV6_NODE, /* Prefix list node. */ - AS_LIST_NODE, /* AS list node. */ - COMMUNITY_LIST_NODE, /* Community list node. */ - RMAP_NODE, /* Route map node. */ - SMUX_NODE, /* SNMP configuration node. */ - DUMP_NODE, /* Packet dump node. */ - FORWARDING_NODE, /* IP forwarding node. */ -#endif + VTY_NODE, /* Vty node. */ - GSMNET_NODE, - BTS_NODE, - TRX_NODE, - TS_NODE, - SUBSCR_NODE, - MGCP_NODE, - GBPROXY_NODE, - SGSN_NODE, - NS_NODE, - BSSGP_NODE, + _LAST_OSMOVTY_NODE }; /* Node which has some commands and prompt string and configuration -- cgit v1.2.3 From c226317c9c7297bebd030af3a7ffd13f2272bc59 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 1 Jun 2010 10:47:07 +0200 Subject: add new new gprs_tmsi2tlli() function --- include/osmocore/gsm_utils.h | 4 +++- src/gsm_utils.c | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/osmocore/gsm_utils.h b/include/osmocore/gsm_utils.h index 51e9f2e6..685bc901 100644 --- a/include/osmocore/gsm_utils.h +++ b/include/osmocore/gsm_utils.h @@ -88,7 +88,7 @@ void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn); uint32_t gsm_gsmtime2fn(struct gsm_time *time); /* GSM TS 03.03 Chapter 2.6 */ -enum gprs_tlli_tyoe { +enum gprs_tlli_type { TLLI_LOCAL, TLLI_FOREIGN, TLLI_RANDOM, @@ -99,5 +99,7 @@ enum gprs_tlli_tyoe { /* TS 03.03 Chapter 2.6 */ int gprs_tlli_type(uint32_t tlli); +uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type); + void generate_backtrace(); #endif diff --git a/src/gsm_utils.c b/src/gsm_utils.c index b392fd37..913946ed 100644 --- a/src/gsm_utils.c +++ b/src/gsm_utils.c @@ -374,3 +374,20 @@ int gprs_tlli_type(uint32_t tlli) return TLLI_RESERVED; } + +uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type) +{ + uint32_t tlli; + switch (type) { + case TLLI_LOCAL: + tlli = p_tmsi | 0xc0000000; + break; + case TLLI_FOREIGN: + tlli = (p_tmsi & 0x3fffffff) | 0x80000000; + break; + default: + tlli = 0; + break; + } + return tlli; +} -- cgit v1.2.3 From dd19527cd2dc49e34973fdefa24acb2d9939e2c1 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Tue, 8 Jun 2010 16:12:58 +0800 Subject: vty: Mark app info strings as const char*. --- include/osmocom/vty/vty.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/osmocom/vty/vty.h b/include/osmocom/vty/vty.h index f0174624..e7399ba1 100644 --- a/include/osmocom/vty/vty.h +++ b/include/osmocom/vty/vty.h @@ -128,9 +128,9 @@ static inline char *vty_newline(struct vty *vty) } struct vty_app_info { - char *name; - char *version; - char *copyright; + const char *name; + const char *version; + const char *copyright; void *tall_ctx; enum node_type (*go_parent_cb)(struct vty *vty); }; -- cgit v1.2.3 From 4f585491b995e4244f85c1ebf10d93443b6ec3fe Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Wed, 9 Jun 2010 13:38:56 +0200 Subject: gsm_utils: Add a fn to test A5(x) support given a classmark2 Signed-off-by: Sylvain Munaut --- include/osmocore/gsm_utils.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/osmocore/gsm_utils.h b/include/osmocore/gsm_utils.h index 685bc901..7dc2388b 100644 --- a/include/osmocore/gsm_utils.h +++ b/include/osmocore/gsm_utils.h @@ -66,6 +66,18 @@ int ms_pwr_dbm(enum gsm_band band, uint8_t lvl); int rxlev2dbm(uint8_t rxlev); uint8_t dbm2rxlev(int dbm); +/* According to GSM 04.08 Chapter 10.5.1.6 */ +static inline int ms_cm2_a5n_support(uint8_t *cm2, int n) { + switch (n) { + case 0: return 1; + case 1: return (cm2[0] & (1<<3)) ? 0 : 1; + case 2: return (cm2[2] & (1<<0)) ? 1 : 0; + case 3: return (cm2[2] & (1<<1)) ? 1 : 0; + default: + return 0; + } +} + /* According to GSM 04.08 Chapter 10.5.2.29 */ static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; } static inline int rach_max_trans_raw2val(int raw) { -- cgit v1.2.3 From d005eabe16b8621b125454fc356ec85e37908b06 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Thu, 10 Jun 2010 13:30:50 +0200 Subject: gsm48: Add authentication response message Signed-off-by: Sylvain Munaut --- include/osmocore/protocol/gsm_04_08.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/osmocore/protocol/gsm_04_08.h b/include/osmocore/protocol/gsm_04_08.h index 20023787..c79b1d03 100644 --- a/include/osmocore/protocol/gsm_04_08.h +++ b/include/osmocore/protocol/gsm_04_08.h @@ -142,6 +142,11 @@ struct gsm48_auth_req { uint8_t rand[16]; } __attribute__ ((packed)); +/* Section 9.2.3 */ +struct gsm48_auth_resp { + uint8_t sres[4]; +} __attribute__ ((packed)); + /* Section 9.2.15 */ struct gsm48_loc_upd_req { uint8_t type:4, -- cgit v1.2.3 From 639f74bdf4d0107f04c30691e2e7749150541681 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 15 Jun 2010 09:32:14 +0200 Subject: [GSM 04.08] add more RR IEIs --- include/osmocore/protocol/gsm_04_08.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/osmocore/protocol/gsm_04_08.h b/include/osmocore/protocol/gsm_04_08.h index c79b1d03..03cee003 100644 --- a/include/osmocore/protocol/gsm_04_08.h +++ b/include/osmocore/protocol/gsm_04_08.h @@ -526,6 +526,15 @@ struct gsm48_imsi_detach_ind { #define GSM48_IE_CHANMODE_7 0x17 #define GSM48_IE_CHANMODE_8 0x18 #define GSM48_IE_CHANDESC_2 0x64 +#define GSM48_IE_MA_AFTER 0x72 +#define GSM48_IE_START_TIME 0x7c +#define GSM48_IE_FREQ_L_BEFORE 0x19 +#define GSM48_IE_CH_DESC_1_BEFORE 0x1c +#define GSM48_IE_CH_DESC_2_BEFORE 0x1d +#define GSM48_IE_F_CH_SEQ_BEFORE 0x1e +#define GSM48_IE_MA_BEFORE 0x21 +#define GSM48_IE_VGCS_T_MODE_I 0x01 + /* FIXME */ /* Section 10.5.4.23 / Table 10.5.130 */ -- cgit v1.2.3 From 93364fc8e0e5cc29e3e639aae345b9f2853a8af8 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 25 Jun 2010 02:50:06 +0200 Subject: [gsm48] fix typo in struct gsm48_req_ref --- include/osmocore/protocol/gsm_04_08.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/osmocore/protocol/gsm_04_08.h b/include/osmocore/protocol/gsm_04_08.h index 03cee003..bdd0ff80 100644 --- a/include/osmocore/protocol/gsm_04_08.h +++ b/include/osmocore/protocol/gsm_04_08.h @@ -56,7 +56,7 @@ struct gsm48_multi_rate_conf { struct gsm48_req_ref { uint8_t ra; uint8_t t3_high:3, - t1_:5; + t1:5; uint8_t t2:5, t3_low:3; } __attribute__ ((packed)); -- cgit v1.2.3 From 816e24cb4296d6b7110da4a89661bbac8dc7af21 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 25 Jun 2010 02:50:56 +0200 Subject: [RSL] Added special RSL message types for mobile side - suspension DL - resume DL - re-establish DL --- include/osmocore/protocol/gsm_08_58.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/osmocore/protocol/gsm_08_58.h b/include/osmocore/protocol/gsm_08_58.h index ca9398f8..7dc35693 100644 --- a/include/osmocore/protocol/gsm_08_58.h +++ b/include/osmocore/protocol/gsm_08_58.h @@ -75,6 +75,10 @@ enum abis_rsl_msgtype { RSL_MT_REL_IND, RSL_MT_UNIT_DATA_REQ, RSL_MT_UNIT_DATA_IND, /* 0x0b */ + RSL_MT_SUSP_REQ, /* non-standard elements */ + RSL_MT_SUSP_CONF, + RSL_MT_RES_REQ, + RSL_MT_RECON_REQ, /* 0x0f */ /* Common Channel Management / TRX Management */ RSL_MT_BCCH_INFO = 0x11, -- cgit v1.2.3