Import version 1.8.3
[s390-tools.git] / vmcp / vmcp.c
blobaf848c8883cb5ed0bf402d4c6371916715b76a9c
1 /*
2 * vmcp
3 * Author: Christian Borntraeger <cborntra@de.ibm.com>
5 * Copyright IBM Corp. 2005, 2006.
7 * Tool for accessing the control program of z/VM using the
8 * kernel module vmcp
9 * return codes:
10 * 0: everything was fine (VMCP_OK)
11 * 1: CP returned a nn zero response code (VMCP_CP)
12 * 2: the response buffer was not large enough (VMCP_BUF)
13 * 3: an internal Linux error occured (VMCP_LIN)
14 * 4: invalid options (VMCP_OPT)
16 * CREDITS: The idea is based on cpint of Neale Fergusson
19 #include <ctype.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <getopt.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/ioctl.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include "vmcp.h"
32 static int keep_case = 0;
33 static int buffersize = 8192;
34 static char command[MAXCMDLEN + 1];
36 static void print_help(const char *name)
38 printf("%s: z/VM CP command interface.\n%s", name, help_text);
41 static void print_version(const char *name)
43 printf("%s: z/VM CP command interface version %s\n"
44 "Copyright IBM Corp. 2005, 2006\n",
45 name, RELEASE_STRING);
48 static void uppercase(char *string)
50 while (*string != '\0') {
51 *string = toupper(*string);
52 string++;
56 /* Parse STRING for buffer size in bytes, allowing size modifier suffix 'k' and
57 * 'm'. Return buffer size in bytes on success, -1 on error. */
58 static long parse_buffersize(char *string)
60 char *suffix;
61 long bytes;
63 bytes = strtol(string, &suffix, 10);
64 if (strlen(suffix) > 1)
65 return -1;
66 switch (*suffix) {
67 case 'k':
68 case 'K':
69 bytes *= 1024;
70 break;
71 case 'm':
72 case 'M':
73 bytes *= 1048576;
74 break;
75 case '\0':
76 break;
77 default:
78 return -1;
80 if ((bytes < MINBUFFER) || (bytes > MAXBUFFER))
81 return -1;
82 return bytes;
85 /* Parse tool parameters. Fill in global variables keep_case, buffersize and
86 * command according to parameters. Return VMCP_OK on success, VMCP_OPT
87 * in case of parameter errors. In case of --help or --version, print
88 * respective text to stdout and exit. */
89 static int parse_args(int argc, char **argv)
91 int opt;
92 int index;
94 do {
95 opt = getopt_long(argc, argv, opt_string, options, NULL);
96 switch (opt) {
97 case -1:
98 /* Reached end of parameter list. */
99 break;
100 case 'h':
101 print_help(argv[0]);
102 exit(VMCP_OK);
103 case 'v':
104 print_version(argv[0]);
105 exit(VMCP_OK);
106 case 'k':
107 keep_case = 1;
108 break;
109 case 'b':
110 buffersize = (int) parse_buffersize(optarg);
111 if (buffersize == -1) {
112 fprintf(stderr, "Error: Invalid buffersize "
113 "(needs to be between %d and %d)\n",
114 MINBUFFER, MAXBUFFER);
115 return VMCP_OPT;
117 break;
118 default:
119 fprintf(stderr, "Try 'vmcp --help' for more"
120 " information.\n");
121 return VMCP_OPT;
123 } while (opt != -1);
124 /* Merge remaining argv contents into command string. */
125 for (index = optind; index < argc; index++) {
126 if (strlen(command) + (strlen(command) == 0 ? 0 : 1) +
127 strlen(argv[index]) > MAXCMDLEN) {
128 fprintf(stderr, "Error: Command too long (cannot be "
129 "longer than %d characters)\n", MAXCMDLEN);
130 return VMCP_OPT;
132 if (strlen(command) > 0)
133 strcat(command, " ");
134 strcat(command, argv[index]);
136 if (strlen(command) == 0) {
137 print_help(argv[0]);
138 return VMCP_OPT;
140 return VMCP_OK;
143 static inline void linux_error(const char *message)
145 fprintf(stderr, "Error: %s: %s\n", message, strerror(errno));
148 /* Read at most COUNT bytes from FD into memory at location BUF. Return
149 * number of bytes read on success, -1 on error. */
150 static ssize_t read_buffer(int fd, char *buf, size_t count)
152 ssize_t ret;
153 ssize_t done;
155 for (done = 0; done < (ssize_t) count; done += ret) {
156 ret = read(fd, &buf[done], count - done);
157 if (ret == -1 && errno == EINTR)
158 continue;
159 if (ret == -1)
160 return -1;
161 if (ret == 0)
162 break;
164 return done;
167 /* Write COUNT bytes to FD from memory at location BUF. Return number of bytes
168 * written on success, -1 otherwise. */
169 static ssize_t write_buffer(int fd, const char *buf, size_t count)
171 ssize_t ret;
172 ssize_t done;
174 for (done = 0; done < (ssize_t) count; done += ret) {
175 ret = write(fd, &buf[done], count - done);
176 if (ret == -1 && errno == EINTR)
177 continue;
178 if (ret == -1)
179 return -1;
180 if (ret == 0)
181 break;
183 return done;
186 int main(int argc, char **argv)
188 int ret;
189 int fd;
190 int response_code;
191 int response_size;
192 char *buffer;
194 ret = parse_args(argc, argv);
195 if (ret != VMCP_OK)
196 return ret;
198 if (!keep_case)
199 uppercase(command);
201 buffer = malloc(buffersize);
202 if (!buffer) {
203 linux_error("Could not allocate return buffer");
204 return VMCP_LIN;
206 fd = open(DEVICE_NODE, O_RDWR);
207 if (fd == -1) {
208 linux_error("Could not open device " DEVICE_NODE);
209 free(buffer);
210 return VMCP_LIN;
212 if (ioctl(fd, VMCP_SETBUF, &buffersize) == -1) {
213 linux_error("Could not set buffer size");
214 free(buffer);
215 close(fd);
216 return VMCP_LIN;
218 if (write(fd, command, strlen(command)) == -1) {
219 linux_error("Could not issue CP command");
220 free(buffer);
221 close(fd);
222 return VMCP_LIN;
224 if (ioctl(fd, VMCP_GETCODE, &response_code) == -1) {
225 linux_error("Could not query return code");
226 free(buffer);
227 close(fd);
228 return VMCP_LIN;
230 if (ioctl(fd, VMCP_GETSIZE, &response_size) == -1) {
231 linux_error("Could not query response size");
232 free(buffer);
233 close(fd);
234 return VMCP_LIN;
236 ret = read_buffer(fd, buffer, buffersize);
237 if (ret == -1) {
238 linux_error("Could not read CP response");
239 free(buffer);
240 close(fd);
241 return VMCP_LIN;
243 write_buffer(STDOUT_FILENO, buffer, ret);
244 if (response_size > buffersize) {
245 fprintf(stderr, "Error: output (%d bytes) was truncated, try "
246 "--buffer to increase size\n", response_size);
247 free(buffer);
248 close(fd);
249 return VMCP_BUF;
251 if (response_code > 0) {
252 fprintf(stderr, "Error: non-zero CP response for command '%s': "
253 "#%d\n", command, response_code);
254 free(buffer);
255 close(fd);
256 return VMCP_CP;
258 free(buffer);
259 close(fd);
260 return VMCP_OK;