//#include //#include #include //#include //#include #include // TODO killme? #include #include #include // TODO killme // //#include //XXX[osmo_counter]#include //XXX[osmo_counter]#include #include #include #include #include #include #include // #include #include #include #include /* per connection data */ LLIST_HEAD(prom_active_connections); static void *prom_ctx; /* Vector which store each vty structure. */ static vector prom_vtyvec; /* per network data */ static int prom_new_connection(struct osmo_fd *fd, unsigned int what); static struct osmo_fd server_socket = { .when = OSMO_FD_READ, .cb = prom_new_connection, .priv_nr = 0, }; /*! Create new vty structure. */ //struct prom_vty *vty_create (int vty_sock, void *priv) // /*! Create new vty structure. */ // TODO extern struct host prom_host; /*! Return if this VTY is a shell or not */ //int prom_vty_shell(struct prom_vty *vty) //{ // return vty->type == VTY_SHELL ? 1 : 0; //} /*! close a telnet connection */ int prom_telnet_close_client(struct osmo_fd *fd) { struct prom_connection *conn = (struct prom_connection*)fd->data; char sock_name_buf[OSMO_SOCK_NAME_MAXLEN]; int rc; /* FIXME: getsockname() always fails: "Bad file descriptor" */ rc = osmo_sock_get_name_buf(sock_name_buf, OSMO_SOCK_NAME_MAXLEN, fd->fd); LOGP(DLGLOBAL, LOGL_INFO, "Closing telnet connection %s\n", (rc <= 0) ? "r=NULL<->l=NULL" : sock_name_buf); close(fd->fd); osmo_fd_unregister(fd); if (conn->dbg) { log_del_target(conn->dbg); talloc_free(conn->dbg); } llist_del(&conn->entry); talloc_free(conn); return 0; } /*! callback from core VTY code about VTY related events */ void prom_vty_event(enum event event, int sock, struct prom_vty *vty) { struct vty_signal_data sig_data; struct prom_connection *connection = vty->priv; struct osmo_fd *bfd; //if (vty->type != VTY_TERM) // return; sig_data.event = event; sig_data.sock = sock; sig_data.vty = vty; osmo_signal_dispatch(SS_L_VTY, S_VTY_EVENT, &sig_data); if (!connection) return; bfd = &connection->fd; switch (event) { case VTY_READ: bfd->when |= OSMO_FD_READ; break; case VTY_WRITE: bfd->when |= OSMO_FD_WRITE; break; case VTY_CLOSED: /* vty layer is about to free() vty */ prom_telnet_close_client(bfd); break; default: break; } } int prom_vty_out_va(struct prom_vty *vty, const char *format, va_list ap) { int len = 0; int size = 1024; char buf[1024]; char *p = NULL; // TODO don't use prom_vty_shell //if (prom_vty_shell(vty)) { // vprintf(format, ap); //} else { va_list args; /* Try to write to initial buffer. */ va_copy(args, ap); 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_copy(args, ap); 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, (unsigned char *) p, len); /* If p is not different with buf, it is allocated buffer. */ if (p != buf) talloc_free(p); //} prom_vty_event(VTY_WRITE, vty->fd, vty); return len; } /*! VTY standard output function * \param[in] vty VTY to which we should print * \param[in] format variable-length format string */ int prom_vty_out(struct prom_vty *vty, const char *format, ...) { va_list args; int rc; va_start(args, format); rc = prom_vty_out_va(vty, format, args); va_end(args); return rc; } /*! Initialize telnet based VTY interface listening to 127.0.0.1 * \param[in] tall_ctx \ref talloc context * \param[in] priv private data to be passed to callback * \param[in] port TCP port number to bind to */ //int telnet_init(void *tall_ctx, void *priv, int port) //{ // return telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port); //} int osmo_prom_init(void *ctx, int port) { return osmo_prom_init_dynif(ctx, "127.0.0.1", port); } /*! Initialize telnet based VTY interface * \param[in] tall_ctx \ref talloc context * \param[in] priv private data to be passed to callback * \param[in] ip IP to listen to ('::1' for localhost, '::0' for all, ...) * \param[in] port TCP port number to bind to */ //int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port) int osmo_prom_init_dynif(void *ctx, const char *host, int port) { int rc; prom_ctx = talloc_named_const(ctx, 1, "prometheus_connection"); rc = osmo_sock_init_ofd( &server_socket, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, host, port, OSMO_SOCK_F_BIND ); server_socket.data = NULL; // könnte priv if (rc < 0) { LOGP(DLGLOBAL, LOGL_ERROR, "Cannot bind telnet at %s %d\n", host, port); return -1; } prom_vtyvec = vector_init(VECTOR_MIN_SIZE); LOGP(DLGLOBAL, LOGL_NOTICE, "Available via HTTP %s %d\n", host, port); return 0; } /*! Unlock the configuration from a given VTY * \param[in] vty VTY from which the configuration shall be unlocked * \returns 0 in case of success */ int prom_vty_config_unlock(struct prom_vty *vty) { //if (vty_config == 1 && vty->config == 1) { // vty->config = 0; // vty_config = 0; //} return vty->config; } void prom_vty_close(struct prom_vty *vty) { int i; /* VTY_CLOSED is handled by the telnet_interface */ prom_vty_event(VTY_CLOSED, vty->fd, vty); 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 < PROM_VTY_MAXHIST; i++) if (vty->hist[i]) talloc_free(vty->hist[i]); /* Unset vector. */ vector_unset(prom_vtyvec, vty->fd); /* Close socket (ignore standard I/O streams). */ if (vty->fd > 2) { close(vty->fd); vty->fd = -1; } if (vty->buf) { talloc_free(vty->buf); vty->buf = NULL; } /* Check configure. */ prom_vty_config_unlock(vty); /* OK free vty. */ talloc_free(vty); } //XXX[osmo_counter]static int osmo_counter_handler(struct osmo_counter *counter, void *vty_) //XXX[osmo_counter]{ //XXX[osmo_counter] struct vty *vty = vty_; //XXX[osmo_counter] const char *description = counter->description; //XXX[osmo_counter] //XXX[osmo_counter] if (!counter->description) //XXX[osmo_counter] description = counter->name; //XXX[osmo_counter] //XXX[osmo_counter] //vty_out(vty, " %s%s: %8lu%s", //XXX[osmo_counter] // vctx->prefix, description, //XXX[osmo_counter] // osmo_counter_get(counter), VTY_NEWLINE); //XXX[osmo_counter] //XXX[osmo_counter] char buf[1024]; //XXX[osmo_counter] //XXX[osmo_counter] // TODO only export current value and let graphing tools sum it up? //XXX[osmo_counter] //XXX[osmo_counter] /* TODO ensure name contains only [a-zA-Z0-] */ //XXX[osmo_counter] const char *name = description; //XXX[osmo_counter] //XXX[osmo_counter] snprintf(buf, sizeof buf, //XXX[osmo_counter] "%s %d\n", //XXX[osmo_counter] name, //XXX[osmo_counter] osmo_counter_get(counter) //XXX[osmo_counter] ); //XXX[osmo_counter] //XXX[osmo_counter] prom_vty_out(vty, "%X\r\n", strlen(buf)); //XXX[osmo_counter] prom_vty_out(vty, "%s\r\n", buf); //XXX[osmo_counter] //XXX[osmo_counter] return 0; //XXX[osmo_counter]} void sanitize_name(char *name) { for (char c; (c = *name) != '\0'; name++) { if ('a' <= c && c <= 'z') continue; if ('A' <= c && c <= 'Z') continue; if ('0' <= c && c <= '9') continue; if (c == ':' || c == '_') continue; *name = '_'; } } static int rate_ctr_handler( struct rate_ctr_group *ctrg, struct rate_ctr *ctr, const struct rate_ctr_desc *desc, void *vty_) { struct vty *vty = vty_; char buf[1024]; char name[1024]; // TODO only export current value and let graphing tools sum it up? strcpy(name, desc->name); sanitize_name(name); char *group = ctrg->desc->group_name_prefix; //char intv_sec_name[1024]; //char intv_min_name[1024]; //char intv_hour_name[1024]; //char intv_day_name[1024]; //strcpy(intv_sec_name, name); //strcpy(intv_min_name, name); //strcpy(intv_hour_name, name); //strcpy(intv_day_name, name); //strcat(intv_sec_name, "_intv_sec"); //strcat(intv_min_name, "_intv_min"); //strcat(intv_hour_name, "_intv_hour"); //strcat(intv_day_name, "_intv_day"); snprintf(buf, sizeof buf, //"%s %d\n" //"%s %d\n" //"%s %d\n" //"%s %d\n" "%s{group=\"%s\"} %d\n", //intv_sec_name, ctr->intv[RATE_CTR_INTV_SEC].rate, //intv_min_name, ctr->intv[RATE_CTR_INTV_MIN].rate, //intv_hour_name, ctr->intv[RATE_CTR_INTV_HOUR].rate, //intv_day_name, ctr->intv[RATE_CTR_INTV_DAY].rate name, group, ctr->current ); prom_vty_out(vty, "%X\r\n", strlen(buf)); prom_vty_out(vty, "%s\r\n", buf); return 0; } static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vty_) { struct vty *vty = vty_; //TODO // if (ctrg->idx) // prom_vty_out(vty, "%s (%d):\r\n", // ctrg->desc->group_description, ctrg->idx); // else // prom_vty_out(vty, "%s:\r\n", // ctrg->desc->group_description); rate_ctr_for_each_counter(ctrg, rate_ctr_handler, vty); return 0; } static int osmo_stat_item_handler( struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *vty_) { struct vty *vty = vty_; const char *unit = item->desc->unit != OSMO_STAT_ITEM_NO_UNIT ? item->desc->unit : ""; char buf[1024]; char name[1024]; //vty_out(vty, " %s%s: %8" PRIi32 " %s%s", // vctx->prefix, item->desc->description, // osmo_stat_item_get_last(item), // unit, VTY_NEWLINE); strcpy(name, item->desc->name); sanitize_name(name); char *group = statg->desc->group_name_prefix; int value = osmo_stat_item_get_last(item); // TODO unit snprintf(buf, sizeof buf, "%s{group=\"%s\",unit=\"%s\"} %d\n", name, group, unit, value); prom_vty_out(vty, "%X\r\n", strlen(buf)); prom_vty_out(vty, "%s\r\n", buf); return 0; } static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *vty_) { struct vty *vty = vty_; //if (statg->idx) // vty_out(vty, "%s%s (%d):%s", vctx->prefix, // statg->desc->group_description, statg->idx, // VTY_NEWLINE); //else // vty_out(vty, "%s%s:%s", vctx->prefix, // statg->desc->group_description, VTY_NEWLINE); osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, vty); return 0; } /*! Read data via vty socket. */ int prom_vty_read(struct prom_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)) { prom_vty_event(VTY_READ, vty_sock, vty); return 0; } } buffer_reset(vty->obuf); vty->status = PROM_VTY_CLOSE; } //LOGP(DLGLOBAL, LOGL_NOTICE, "read %d byte(s)\n", nbytes); prom_vty_out(vty, "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n" "Transfer-Encoding: chunked\r\n" // "Content-Length: 6\r\n" "\r\n" ); //vty->status = PROM_VTY_CLOSE; //XXX[osmo_counter]osmo_counters_for_each(osmo_counter_handler, vty); rate_ctr_for_each_group(rate_ctr_group_handler, vty); osmo_stat_item_for_each_group(osmo_stat_item_group_handler, vty); /* finish chunked transfer encoding */ prom_vty_out(vty, "0\r\n\r\n"); // TODO parse /* Check status. */ if (vty->status == PROM_VTY_CLOSE) { prom_vty_close(vty); return -EBADF; } else { prom_vty_event(VTY_WRITE, vty_sock, vty); prom_vty_event(VTY_READ, vty_sock, vty); } return 0; } static int client_data(struct osmo_fd *fd, unsigned int what) { struct prom_connection *conn = fd->data; int rc = 0; if (what & OSMO_FD_READ) { conn->fd.when &= ~OSMO_FD_READ; // TODO feed server rc = prom_vty_read(conn->vty); } /* vty might have been closed from vithin prom_vty_read() */ if (rc == -EBADF) return rc; if (what & OSMO_FD_WRITE) { rc = buffer_flush_all(conn->vty->obuf, fd->fd); if (rc == BUFFER_EMPTY) conn->fd.when &= ~OSMO_FD_WRITE; } return rc; } /*! Allocate a new vty interface structure */ struct prom_vty *prom_vty_new(void) { struct prom_vty *new = talloc_zero(tall_vty_ctx, struct vty); if (!new) goto out; INIT_LLIST_HEAD(&new->parent_nodes); new->obuf = buffer_new(new, 0); /* Use default buffer size. */ if (!new->obuf) goto out_new; new->buf = _talloc_zero(new, PROM_HTTP_BUFSIZ, "prom_vty_new->buf"); if (!new->buf) goto out_obuf; new->max = PROM_HTTP_BUFSIZ; new->fd = -1; return new; out_obuf: buffer_free(new->obuf); out_new: talloc_free(new); new = NULL; out: return new; } struct vty * prom_vty_create (int vty_sock, void *priv) { struct prom_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 = prom_vty_new (); vty->fd = vty_sock; vty->priv = priv; //vty->type = VTY_TERM; // TODO kill vty->type //if (!password_check) // { // if (prom_host.advanced) // vty->node = ENABLE_NODE; // else // vty->node = VIEW_NODE; // } //else // vty->node = AUTH_NODE; vty->fail = 0; vty->cp = 0; // TODO vty_clear_buf (vty); vty->length = 0; memset (vty->hist, 0, sizeof (vty->hist)); vty->hp = 0; vty->hindex = 0; vector_set_index (prom_vtyvec, vty_sock, vty); vty->status = PROM_VTY_NORMAL; //if (prom_host.lines >= 0) // vty->lines = prom_host.lines; //else // vty->lines = -1; //if (password_check) // { // /* Vty is not available if password isn't set. */ // if (prom_host.password == NULL && prom_host.password_encrypt == NULL) // { // prom_vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); // vty->status = PROM_VTY_CLOSE; // prom_vty_close (vty); // return NULL; // } // } /* Say hello to the world. */ //prom_vty_hello (vty); //if (password_check) // prom_vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); /* Setting up terminal. */ // TODO vty_will_echo (vty); //TODO vty_will_suppress_go_ahead (vty); // TODO vty_dont_linemode (vty); // TODO vty_do_window_size (vty); /* vty_dont_lflow_ahead (vty); */ // TODO vty_prompt (vty); /* Add read/write thread. */ prom_vty_event (VTY_WRITE, vty_sock, vty); prom_vty_event (VTY_READ, vty_sock, vty); return vty; } static int prom_new_connection(struct osmo_fd *fd, unsigned int what) { //LOGP(DLGLOBAL, LOGL_ERROR, "derp\n"); struct prom_connection *connection; struct sockaddr_in sockaddr; socklen_t len = sizeof(sockaddr); int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len); char sock_name_buf[OSMO_SOCK_NAME_MAXLEN]; int rc; if (new_connection < 0) { LOGP(DLGLOBAL, LOGL_ERROR, "HTTP accept failed\n"); return new_connection; } rc = osmo_sock_get_name_buf(sock_name_buf, OSMO_SOCK_NAME_MAXLEN, new_connection); LOGP(DLGLOBAL, LOGL_INFO, "Accept()ed new HTTP connection %s\n", (rc <= 0) ? "r=NULL<->l=NULL" : sock_name_buf); connection = talloc_zero(prom_ctx, struct prom_connection); connection->priv = fd->data; connection->fd.data = connection; connection->fd.fd = new_connection; connection->fd.when = OSMO_FD_READ; connection->fd.cb = client_data; rc = osmo_fd_register(&connection->fd); if (rc < 0) { talloc_free(connection); return rc; } llist_add_tail(&connection->entry, &prom_active_connections); // TODO create server connection->vty = prom_vty_create(new_connection, connection); if (!connection->vty) { LOGP(DLGLOBAL, LOGL_ERROR, "couldn't create VTY\n"); /* prom_vty_create() is already closing the fd if it returns NULL */ talloc_free(connection); return -1; } return 0; }