diff --git a/.gitignore b/.gitignore index 9e287803..41feaf6b 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ libutil/util_prg_example libutil/util_rec_example libutil/util_scandir_example libzds/libzds.a +libvmcp/vmcp_example mon_tools/mon_fsstatd mon_tools/mon_procd osasnmpd/osasnmpd diff --git a/Makefile b/Makefile index 3c9e609d..adc92b61 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar # Include common definitions include common.mak -LIB_DIRS = libvtoc libu2s libutil libzds libdasd libvmdump libccw +LIB_DIRS = libvtoc libu2s libutil libzds libdasd libvmdump libccw libvmcp TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ diff --git a/common.mak b/common.mak index c71a65ba..43a0d6e2 100644 --- a/common.mak +++ b/common.mak @@ -338,6 +338,10 @@ $(rootdir)/libvmdump/libvmdump.a: $(rootdir)/libvmdump $(MAKE) -C $(rootdir)/libvmdump/ libvmdump.a .PHONY: $(rootdir)/libvmdump +$(rootdir)/libvmcp/libvmcp.a: $(rootdir)/libvmcp + $(MAKE) -C $(rootdir)/libvmcp/ libvmcp.a +.PHONY: $(rootdir)/libvmcp + $(rootdir)/zipl/boot/data.o: $(MAKE) -C $(rootdir)/zipl/boot/ data.o diff --git a/include/lib/vmcp.h b/include/lib/vmcp.h new file mode 100644 index 00000000..1b11d4df --- /dev/null +++ b/include/lib/vmcp.h @@ -0,0 +1,52 @@ +/* + * vmcp - z/VM CP command library + * + * Copyright IBM Corp. 2005, 2018 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIB_VMCP_H +#define LIB_VMCP_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define VMCP_DEVICE_NODE "/dev/vmcp" /* VMCP device name */ +#define VMCP_DEFAULT_BUFSZ 0x4000 /* VMCP default buffer size */ + +/* + * Error codes returned by vmcp() function. This enables the caller + * to emit a detailed error message on the type of failure. + */ +#define VMCP_SUCCESS 0 /* VMCP operation completed successfully */ +#define VMCP_ERR_OPEN (-1) /* Error Open of VMCP device */ +#define VMCP_ERR_SETBUF (-2) /* Error VMCP_SETBUF ioctl */ +#define VMCP_ERR_READ (-3) /* Error VMCP read system call */ +#define VMCP_ERR_GETCODE (-4) /* Error VMCP_GETCODE ioctl */ +#define VMCP_ERR_GETSIZE (-5) /* Error VMCP_GETSIZE ioctl */ +#define VMCP_ERR_TOOSMALL (-6) /* Error VMCP buffer too small for + * response + */ +#define VMCP_ERR_WRITE (-7) /* Error VMCP write system call */ + +struct vmcp_parm { + const char *cpcmd; + unsigned int buffer_size; + bool do_upper; + int cprc; + char *response; + unsigned int response_size; +}; + +int vmcp(struct vmcp_parm *cp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libvmcp/Makefile b/libvmcp/Makefile new file mode 100644 index 00000000..88cdb31b --- /dev/null +++ b/libvmcp/Makefile @@ -0,0 +1,18 @@ +include ../common.mak + +lib = libvmcp.a + +examples = vmcp_example + +all: $(lib) $(examples) + +objects = vmcp.o + +$(lib): $(objects) + +install: all + +vmcp_example: vmcp_example.o $(lib) $(rootdir)/libutil/libutil.a + +clean: + rm -f *.o $(lib) $(examples) diff --git a/libvmcp/vmcp.c b/libvmcp/vmcp.c new file mode 100644 index 00000000..a86a6c66 --- /dev/null +++ b/libvmcp/vmcp.c @@ -0,0 +1,134 @@ +/* + * vmcp - z/VM CP function library + * + * Copyright IBM Corp. 2018 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "lib/util_libc.h" +#include "lib/vmcp.h" + +#define VMCP_DEVICE_NODE "/dev/vmcp" +#define VMCP_GETSIZE _IOR(0x10, 3, int) +#define VMCP_SETBUF _IOW(0x10, 2, int) +#define VMCP_GETCODE _IOR(0x10, 1, int) + +/* + * Read at most COUNT bytes from FD into memory at location BUF. + * Return number of bytes read on success, -1 on error. + */ +static ssize_t read_buffer(int fd, char *buf, ssize_t count) +{ + ssize_t ret, done; + + for (done = 0; done < (ssize_t)count; done += ret) { + ret = read(fd, &buf[done], count - done); + if (ret == -1 && errno == EINTR) + continue; + if (ret == -1) + return -1; + if (ret == 0) + break; + } + return done; +} + +/** + * Submit a command to z/VM CP and return the response, the size in bytes + * of the response and CP command response code. + * + * The member response contains the response from CP. It is a pointer + * to a malloc'ed buffer which must be freed by the caller when the return + * code is zero or VMCP_ERR_TOOSMALL. + * + * @param[in,out] cmd Pointer to struct vmcp_parm. + * @param[in] cpcmd Pointer to CP command string. + * @param[in] do_upper If true convert cpcmd string to upper case. + * @param[out] cprc Return code of the CP command. + * @param[out] response Return buffer, has been allocated via malloc() + * must be freed by caller, see below. + * @param[out] response_size Size of the response in bytes, can be larger + * than buffer_size. + * + * @returns Zero on success or a negative error number on failure. + * @retval VMCP_SUCCESS Success valid members: cprc, response, + * response_size + * @retval VMCP_ERR_OPEN Error opening VMCP device, valid members: none + * @retval VMCP_ERR_SETBUF Error setting buffer, valid members: none + * @retval VMCP_ERR_GETCODE Error getting cp exit code, valid members: none + * @retval VMCP_ERR_WRITE Error write CP command, valid members: none + * @retval VMCP_ERR_GETSIZE Error reading response size, + * valid members: cprc + * @retval VMCP_ERR_READ Error reading resp + * @retval VMCP_ERR_TOOSMALL Error response buffer too small, response + * truncated, valid members: cprc, response, + * response_size + */ +int vmcp(struct vmcp_parm *cmd) +{ + int rc = VMCP_SUCCESS, fd, len; + char *cpcmd; + + cmd->response = NULL; + cmd->response_size = 0; + cmd->cprc = 0; + + fd = open(VMCP_DEVICE_NODE, O_RDWR); + if (fd == -1) + return VMCP_ERR_OPEN; + + cpcmd = util_strdup(cmd->cpcmd); /* Exits on failure */ + if (cmd->do_upper) + util_str_toupper(cpcmd); + + if (ioctl(fd, VMCP_SETBUF, &cmd->buffer_size) == -1) { + rc = VMCP_ERR_SETBUF; + goto fail; + } + + if (write(fd, cpcmd, strlen(cpcmd)) == -1) { + rc = VMCP_ERR_WRITE; + goto fail; + } + + if (ioctl(fd, VMCP_GETCODE, &cmd->cprc) == -1) { + rc = VMCP_ERR_GETCODE; + goto fail; + } + + if (ioctl(fd, VMCP_GETSIZE, &cmd->response_size) == -1) { + rc = VMCP_ERR_GETSIZE; + goto fail; + } + + if (cmd->response_size <= cmd->buffer_size) { + len = cmd->response_size; + } else { + len = cmd->buffer_size; + rc = VMCP_ERR_TOOSMALL; + } + + /* Exits on failure */ + cmd->response = (char *)util_zalloc(len + 1); + + if (read_buffer(fd, cmd->response, len) == -1) { + rc = VMCP_ERR_READ; + free(cmd->response); + cmd->response = NULL; + } + +fail: + close(fd); + free(cpcmd); + return rc; +} diff --git a/libvmcp/vmcp_example.c b/libvmcp/vmcp_example.c new file mode 100644 index 00000000..a3c0eea9 --- /dev/null +++ b/libvmcp/vmcp_example.c @@ -0,0 +1,65 @@ +/** + * vmcp_example - Example program for vmcp.c + * + * Copyright 2018 IBM Corp. + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +//! [code] +#include +#include +#include + +#include "lib/vmcp.h" + +/* + * Demonstrate vmcp() function usage + */ +int main(void) +{ + struct vmcp_parm cp; + int rc; + + cp.cpcmd = "q osa"; + cp.buffer_size = VMCP_DEFAULT_BUFSZ; + cp.do_upper = true; + + rc = vmcp(&cp); + + switch (rc) { + case VMCP_SUCCESS: + printf("%s\n", cp.response); + free(cp.response); + break; + case VMCP_ERR_OPEN: + errx(EXIT_FAILURE, "Could not open device %s", VMCP_DEVICE_NODE); + break; + case VMCP_ERR_SETBUF: + errx(EXIT_FAILURE, "Could not set buffer size"); + break; + case VMCP_ERR_GETCODE: + errx(EXIT_FAILURE, "Could not query return code"); + break; + case VMCP_ERR_GETSIZE: + errx(EXIT_FAILURE, "Could not query response size"); + break; + case VMCP_ERR_WRITE: + errx(EXIT_FAILURE, "Could not issue CP command"); + break; + case VMCP_ERR_READ: + errx(EXIT_FAILURE, "Could not read CP response"); + break; + case VMCP_ERR_TOOSMALL: + free(cp.response); + printf("Response truncated (requires %u bytes)\n", + cp.response_size); + errx(EXIT_FAILURE, "Response buffer (%u bytes) too small", + cp.buffer_size); + break; + } + return EXIT_SUCCESS; +} + +//! [code]