Whamcloud - gitweb
LU-11109 mdt: handle zero length xattr values correctly
[fs/lustre-release.git] / lustre / tests / multiop.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2012, 2017, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  * Lustre is a trademark of Sun Microsystems, Inc.
31  */
32
33 #ifndef _GNU_SOURCE
34 #define _GNU_SOURCE /* pull in O_DIRECTORY in bits/fcntl.h */
35 #endif
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <limits.h>
39 #include <malloc.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/mman.h>
45 #include <sys/vfs.h>
46 #include <sys/ioctl.h>
47 #include <sys/xattr.h>
48 #include <sys/file.h>
49 #include <signal.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <semaphore.h>
53 #include <time.h>
54 #include <err.h>
55
56 #include <lustre/lustreapi.h>
57
58 #define T1 "write data before unlink\n"
59 #define T2 "write data after unlink\n"
60 char msg[] = "yabba dabba doo, I'm coming for you, I live in a shoe, I don't know what to do.\n'Bigger, bigger,and bigger yet!' cried the Creator.  'You are not yet substantial enough for my boundless intents!'  And ever greater and greater the object became, until all was lost 'neath its momentus bulk.\n";
61 char *buf, *buf_align;
62 int bufsize = 0;
63 sem_t sem;
64 #define ALIGN_LEN 65535
65 #define XATTR "user.multiop"
66
67 char usage[] =
68 "Usage: %s filename command-sequence [path...]\n"
69 "    command-sequence items:\n"
70 "        A  fsetxattr(\"user.multiop\")\n"
71 "        a[num] fgetxattr(\"user.multiop\") [optional buffer size, default 0]\n"
72 "        c  close\n"
73 "        B[num] call setstripe ioctl to create stripes\n"
74 "        C[num] create with optional stripes\n"
75 "        d  mkdir\n"
76 "        D  open(O_DIRECTORY)\n"
77 "        e[R|W|U] apply lease. R: Read; W: Write; U: Unlock\n"
78 "        E[+|-] get lease. +/-: expect lease to (not) exist\n"
79 "        f  statfs\n"
80 "        F  print FID\n"
81 "        H[num] create HSM released file with num stripes\n"
82 "        G gid get grouplock\n"
83 "        g gid put grouplock\n"
84 "        K  link path to filename\n"
85 "        L  link\n"
86 "        l  symlink filename to path\n"
87 "        m  mknod\n"
88 "        M  rw mmap to EOF (must open and stat prior)\n"
89 "        n  rename path to filename\n"
90 "        N  rename filename to path\n"
91 "        o  open(O_RDONLY)\n"
92 "        O  open(O_CREAT|O_RDWR)\n"
93 "        p  print return value of last command\n"
94 "        r[num] read [optional length]\n"
95 "        R  reference entire mmap-ed region\n"
96 "        s  stat\n"
97 "        S  fstat\n"
98 "        t  fchmod\n"
99 "        T[num] ftruncate [optional position, default 0]\n"
100 "        u  unlink\n"
101 "        U  munmap\n"
102 "        v  verbose\n"
103 "        V  open a volatile file\n"
104 "        w[num] write optional length\n"
105 "        x  get file data version\n"
106 "        W  write entire mmap-ed region\n"
107 "        y  fsync\n"
108 "        Y  fdatasync\n"
109 "        z[num] lseek(SEEK_SET) [optional offset, default 0]\n"
110 "        Z[num] lseek(SEEK_CUR) [optional offset, default 0]\n"
111 "        _  wait for signal\n";
112
113 void usr1_handler(int unused)
114 {
115         int saved_errno = errno;
116
117         /*
118          * signal(7): POSIX.1-2004 ...requires an implementation to guarantee
119          * that the following functions can be safely called inside a signal
120          * handler:
121          *            sem_post()
122          */
123         sem_post(&sem);
124
125         errno = saved_errno;
126 }
127
128 static const char *
129 pop_arg(int argc, char *argv[])
130 {
131         static int cur_arg = 3;
132
133         if (cur_arg >= argc)
134                 return NULL;
135
136         return argv[cur_arg++];
137 }
138
139 struct flag_mapping {
140         const char *string;
141         const int  flag;
142 } flag_table[] = {
143         {"O_RDONLY", O_RDONLY},
144         {"O_WRONLY", O_WRONLY},
145         {"O_RDWR", O_RDWR},
146         {"O_CREAT", O_CREAT},
147         {"O_EXCL", O_EXCL},
148         {"O_NOCTTY", O_NOCTTY},
149         {"O_TRUNC", O_TRUNC},
150         {"O_APPEND", O_APPEND},
151         {"O_NONBLOCK", O_NONBLOCK},
152         {"O_NDELAY", O_NDELAY},
153         {"O_SYNC", O_SYNC},
154 #ifdef O_DIRECT
155         {"O_DIRECT", O_DIRECT},
156 #endif
157 #ifdef O_NOATIME
158         {"O_NOATIME", O_NOATIME},
159 #endif
160         {"O_LARGEFILE", O_LARGEFILE},
161         {"O_DIRECTORY", O_DIRECTORY},
162         {"O_NOFOLLOW", O_NOFOLLOW},
163         {"O_LOV_DELAY_CREATE", O_LOV_DELAY_CREATE},
164         {"", -1}
165 };
166
167 int get_flags(char *data, int *rflags)
168 {
169         char *cloned_flags;
170         char *tmp;
171         int flag_set = 0;
172         int flags = 0;
173         int size = 0;
174
175         cloned_flags = strdup(data);
176         if (cloned_flags == NULL) {
177                 fprintf(stderr, "Insufficient memory.\n");
178                 exit(-1);
179         }
180
181         for (tmp = strtok(cloned_flags, ":"); tmp;
182              tmp = strtok(NULL, ":")) {
183                 int i;
184
185                 size = tmp - cloned_flags;
186                 for (i = 0; flag_table[i].flag != -1; i++) {
187                         if (!strcmp(tmp, flag_table[i].string)){
188                                 flags |= flag_table[i].flag;
189                                 size += strlen(flag_table[i].string);
190                                 flag_set = 1;
191                                 break;
192                         }
193                 }
194         }
195         free(cloned_flags);
196
197         if (!flag_set) {
198                 *rflags = O_RDONLY;
199                 return 0;
200         }
201
202         *rflags = flags;
203         return size;
204 }
205
206 #define POP_ARG() (pop_arg(argc, argv))
207
208 int main(int argc, char **argv)
209 {
210         char                    *fname, *commands;
211         const char              *newfile;
212         const char              *oldpath;
213         struct stat              st;
214         struct statfs            stfs;
215         size_t                   mmap_len = 0, i;
216         unsigned char           *mmap_ptr = NULL, junk = 0;
217         int                      len, fd = -1;
218         int                      flags;
219         int                      save_errno;
220         int                      verbose = 0;
221         int                      gid = 0;
222         struct lu_fid            fid;
223         struct timespec          ts;
224         struct lov_user_md_v3    lum;
225         char *xattr_buf = NULL;
226         size_t xattr_buf_size = 0;
227         long long rc = 0;
228         long long last_rc;
229
230         if (argc < 3) {
231                 fprintf(stderr, usage, argv[0]);
232                 exit(1);
233         }
234
235         memset(&st, 0, sizeof(st));
236         sem_init(&sem, 0, 0);
237         /* use sigaction instead of signal to avoid SA_ONESHOT semantics */
238         sigaction(SIGUSR1, &(const struct sigaction){.sa_handler = &usr1_handler},
239                   NULL);
240
241         fname = argv[1];
242
243         for (commands = argv[2]; *commands; commands++) {
244                 /* XXX Most commands return 0 or we exit so we only
245                  * update rc where really needed. */
246                 last_rc = rc;
247                 rc = 0;
248
249                 switch (*commands) {
250                 case '_':
251                         if (verbose) {
252                                 printf("PAUSING\n");
253                                 fflush(stdout);
254                         }
255                         len = atoi(commands+1);
256                         if (len <= 0)
257                                 len = 3600; /* 1 hour */
258                         ts.tv_sec = time(NULL) + len;
259                         ts.tv_nsec = 0;
260                         while (sem_timedwait(&sem, &ts) < 0 && errno == EINTR);
261                         break;
262                 case 'A':
263                         if (fsetxattr(fd, XATTR, "multiop", 8, 0)) {
264                                 save_errno = errno;
265                                 perror("fsetxattr");
266                                 exit(save_errno);
267                         }
268                         break;
269                 case 'a':
270                         len = atoi(commands + 1);
271                         if (xattr_buf_size < len) {
272                                 xattr_buf = realloc(xattr_buf, len);
273                                 if (xattr_buf == NULL) {
274                                         save_errno = errno;
275                                         perror("allocating xattr buffer\n");
276                                         exit(save_errno);
277                                 }
278
279                                 xattr_buf_size = len;
280                         }
281
282                         rc = fgetxattr(fd, XATTR, xattr_buf, len);
283                         if (rc < 0) {
284                                 save_errno = errno;
285                                 perror("fgetxattr");
286                                 exit(save_errno);
287                         }
288                         break;
289                 case 'c':
290                         if (close(fd) == -1) {
291                                 save_errno = errno;
292                                 perror("close");
293                                 exit(save_errno);
294                         }
295                         fd = -1;
296                         break;
297                 case 'B':
298                         lum = (struct lov_user_md_v3) {
299                                 .lmm_magic = LOV_USER_MAGIC_V3,
300                                 .lmm_stripe_count = atoi(commands + 1),
301                         };
302
303                         if (ioctl(fd, LL_IOC_LOV_SETSTRIPE, &lum) < 0) {
304                                 save_errno = errno;
305                                 perror("LL_IOC_LOV_SETSTRIPE");
306                                 exit(save_errno);
307                         }
308                         break;
309                 case 'C':
310                         len = atoi(commands+1);
311                         fd = llapi_file_open(fname, O_CREAT | O_WRONLY, 0644,
312                                              0, 0, len, 0);
313                         if (fd == -1) {
314                                 save_errno = errno;
315                                 perror("create stripe file");
316                                 exit(save_errno);
317                         }
318                         rc = fd;
319                         break;
320                 case 'd':
321                         if (mkdir(fname, 0755) == -1) {
322                                 save_errno = errno;
323                                 perror("mkdir(0755)");
324                                 exit(save_errno);
325                         }
326                         break;
327                 case 'D':
328                         fd = open(fname, O_DIRECTORY);
329                         if (fd == -1) {
330                                 save_errno = errno;
331                                 perror("open(O_DIRECTORY)");
332                                 exit(save_errno);
333                         }
334                         rc = fd;
335                         break;
336                 case 'e':
337                         commands++;
338                         switch (*commands) {
339                         case 'U':
340                                 rc = llapi_lease_release(fd);
341                                 break;
342                         case 'R':
343                                 rc = llapi_lease_acquire(fd, LL_LEASE_RDLCK);
344                                 break;
345                         case 'W':
346                                 rc = llapi_lease_acquire(fd, LL_LEASE_WRLCK);
347                                 break;
348                         default:
349                                 errx(-1, "unknown mode: %c", *commands);
350                         }
351                         if (rc < 0)
352                                 err(errno, "apply/unlock lease error");
353
354                         if (flags != LL_LEASE_UNLCK)
355                                 break;
356
357                         /* F_UNLCK, interpret return code */
358                         if (rc > 0) {
359                                 const char *str = "unknown";
360                                 if (rc == LL_LEASE_RDLCK)
361                                         str = "read";
362                                 else if (rc == LL_LEASE_WRLCK)
363                                         str = "write";
364                                 fprintf(stdout, "%s lease(%lld) released.\n",
365                                         str, rc);
366                         } else if (rc == 0) {
367                                 fprintf(stdout, "lease already broken.\n");
368                         }
369                         break;
370                 case 'E':
371                         commands++;
372                         if (*commands != '-' && *commands != '+')
373                                 errx(-1, "unknown mode: %c\n", *commands);
374
375                         rc = llapi_lease_check(fd);
376                         if (rc > 0) {
377                                 const char *str = "unknown";
378
379                                 if (rc == LL_LEASE_RDLCK)
380                                         str = "read";
381                                 else if (rc == LL_LEASE_WRLCK)
382                                         str = "write";
383                                 fprintf(stdout, "%s lease(%lld) has applied.\n",
384                                         str, rc);
385                                 if (*commands == '-')
386                                         errx(-1, "expect lease to not exist");
387                         } else if (rc == 0) {
388                                 fprintf(stdout, "no lease applied.\n");
389                                 if (*commands == '+')
390                                         errx(-1, "expect lease exists");
391                         } else {
392                                 err(errno, "free lease error");
393                         }
394                         break;
395                 case 'f':
396                         if (statfs(fname, &stfs) == -1)
397                                 errx(-1, "statfs()");
398                         break;
399                 case 'F':
400                         if (fd == -1)
401                                 rc = llapi_path2fid(fname, &fid);
402                         else
403                                 rc = llapi_fd2fid(fd, &fid);
404                         if (rc != 0)
405                                 fprintf(stderr,
406                                         "llapi_path/fd2fid() on %d, rc=%lld\n",
407                                         fd, rc);
408                         else
409                                 printf(DFID"\n", PFID(&fid));
410                         break;
411                 case 'G':
412                         gid = atoi(commands+1);
413                         if (ioctl(fd, LL_IOC_GROUP_LOCK, gid) == -1) {
414                                 save_errno = errno;
415                                 perror("ioctl(GROUP_LOCK)");
416                                 exit(save_errno);
417                         }
418                         break;
419                 case 'g':
420                         gid = atoi(commands+1);
421                         if (ioctl(fd, LL_IOC_GROUP_UNLOCK, gid) == -1) {
422                                 save_errno = errno;
423                                 perror("ioctl(GROUP_UNLOCK)");
424                                 exit(save_errno);
425                         }
426                         break;
427                 case 'H':
428                         len = atoi(commands+1);
429                         fd = llapi_file_open(fname, O_CREAT | O_WRONLY,
430                                 0644, 0, 0, len,
431                                 LOV_PATTERN_RAID0 | LOV_PATTERN_F_RELEASED);
432                         if (fd == -1) {
433                                 save_errno = errno;
434                                 perror("create stripe file");
435                                 exit(save_errno);
436                         }
437                         rc = fd;
438                         break;
439                 case 'j':
440                         if (flock(fd, LOCK_EX) == -1)
441                                 errx(-1, "flock()");
442                         break;
443                 case 'K':
444                         oldpath = POP_ARG();
445                         if (oldpath == NULL)
446                                 oldpath = fname;
447
448                         if (link(oldpath, fname)) {
449                                 save_errno = errno;
450                                 perror("link()");
451                                 exit(save_errno);
452                         }
453                         break;
454                 case 'l':
455                         newfile = POP_ARG();
456                         if (!newfile)
457                                 newfile = fname;
458                         if (symlink(fname, newfile)) {
459                                 save_errno = errno;
460                                 perror("symlink()");
461                                 exit(save_errno);
462                         }
463                         break;
464                 case 'L':
465                         newfile = POP_ARG();
466                         if (newfile == NULL)
467                                 newfile = fname;
468
469                         if (link(fname, newfile)) {
470                                 save_errno = errno;
471                                 perror("link()");
472                                 exit(save_errno);
473                         }
474                         break;
475                 case 'm':
476                         if (mknod(fname, S_IFREG | 0644, 0) == -1) {
477                                 save_errno = errno;
478                                 perror("mknod(S_IFREG|0644, 0)");
479                                 exit(save_errno);
480                         }
481                         break;
482                 case 'M':
483                         if (st.st_size == 0) {
484                                 fprintf(stderr, "mmap without preceeding stat, or on"
485                                         " zero length file.\n");
486                                 exit(-1);
487                         }
488                         mmap_len = st.st_size;
489                         mmap_ptr = mmap(NULL, mmap_len, PROT_WRITE | PROT_READ,
490                                         MAP_SHARED, fd, 0);
491                         if (mmap_ptr == MAP_FAILED) {
492                                 save_errno = errno;
493                                 perror("mmap");
494                                 exit(save_errno);
495                         }
496                         break;
497                 case 'n':
498                         oldpath = POP_ARG();
499                         if (oldpath == NULL)
500                                 oldpath = fname;
501
502                         if (rename(oldpath, fname) < 0) {
503                                 save_errno = errno;
504                                 perror("rename()");
505                                 exit(save_errno);
506                         }
507                         break;
508                 case 'N':
509                         newfile = POP_ARG();
510                         if (!newfile)
511                                 newfile = fname;
512                         if (rename (fname, newfile)) {
513                                 save_errno = errno;
514                                 perror("rename()");
515                                 exit(save_errno);
516                         }
517                         break;
518                 case 'O':
519                         fd = open(fname, O_CREAT|O_RDWR, 0644);
520                         if (fd == -1) {
521                                 save_errno = errno;
522                                 perror("open(O_RDWR|O_CREAT)");
523                                 exit(save_errno);
524                         }
525                         rc = fd;
526                         break;
527                 case 'o':
528                         len = get_flags(commands+1, &flags);
529                         commands += len;
530                         if (flags & O_CREAT)
531                                 fd = open(fname, flags, 0666);
532                         else
533                                 fd = open(fname, flags);
534                         if (fd == -1) {
535                                 save_errno = errno;
536                                 perror("open");
537                                 exit(save_errno);
538                         }
539                         rc = fd;
540                         break;
541                 case 'p':
542                         printf("%lld\n", last_rc);
543                         break;
544                 case 'r':
545                         len = atoi(commands+1);
546                         if (len <= 0)
547                                 len = 1;
548                         if (bufsize < len) {
549                                 void *tmp;
550                                 tmp = realloc(buf, len + ALIGN_LEN);
551                                 if (tmp == NULL) {
552                                         free(buf);
553                                         save_errno = errno;
554                                         perror("allocating buf for read\n");
555                                         exit(save_errno);
556                                 }
557                                 buf = tmp;
558                                 bufsize = len;
559                                 buf_align = (char *)((long)(buf + ALIGN_LEN) &
560                                                      ~ALIGN_LEN);
561                         }
562                         while (len > 0) {
563                                 rc = read(fd, buf_align, len);
564                                 if (rc == -1) {
565                                         save_errno = errno;
566                                         perror("read");
567                                         exit(save_errno);
568                                 }
569                                 if (rc < len) {
570                                         fprintf(stderr, "short read: %lld/%u\n",
571                                                 rc, len);
572                                         if (rc == 0)
573                                                 exit(ENODATA);
574                                 }
575                                 len -= rc;
576                                 if (verbose >= 2)
577                                         printf("%.*s\n", (int)rc, buf_align);
578                         }
579                         break;
580                 case 'R':
581                         for (i = 0; i < mmap_len && mmap_ptr; i += 4096)
582                                 junk += mmap_ptr[i];
583                         break;
584                 case 's':
585                         if (stat(fname, &st) == -1) {
586                                 save_errno = errno;
587                                 perror("stat");
588                                 exit(save_errno);
589                         }
590                         break;
591                 case 'S':
592                         if (fstat(fd, &st) == -1) {
593                                 save_errno = errno;
594                                 perror("fstat");
595                                 exit(save_errno);
596                         }
597                         break;
598                 case 't':
599                         if (fchmod(fd, 0) == -1) {
600                                 save_errno = errno;
601                                 perror("fchmod");
602                                 exit(save_errno);
603                         }
604                         break;
605                 case 'T':
606                         len = atoi(commands+1);
607                         if (ftruncate(fd, len) == -1) {
608                                 save_errno = errno;
609                                 printf("ftruncate (%d,%d)\n", fd, len);
610                                 perror("ftruncate");
611                                 exit(save_errno);
612                         }
613                         break;
614                 case 'u':
615                         if (unlink(fname) == -1) {
616                                 save_errno = errno;
617                                 perror("unlink");
618                                 exit(save_errno);
619                         }
620                         break;
621                 case 'U':
622                         if (munmap(mmap_ptr, mmap_len)) {
623                                 save_errno = errno;
624                                 perror("munmap");
625                                 exit(save_errno);
626                         }
627                         break;
628                 case 'v':
629                         verbose++;
630                         break;
631                 case 'V':
632                         len = get_flags(commands + 1, &flags);
633                         commands += len;
634                         fd = llapi_create_volatile(fname, flags);
635                         if (fd < 0) {
636                                 perror("llapi_create_volatile");
637                                 exit(fd);
638                         }
639                         rc = fd;
640                         break;
641                 case 'w':
642                         len = atoi(commands+1);
643                         if (len <= 0)
644                                 len = 1;
645                         if (bufsize < len) {
646                                 void *tmp;
647                                 tmp = realloc(buf, len + ALIGN_LEN);
648                                 if (tmp == NULL) {
649                                         free(buf);
650                                         save_errno = errno;
651                                         perror("allocating buf for write\n");
652                                         exit(save_errno);
653                                 }
654                                 buf = tmp;
655                                 bufsize = len;
656                                 buf_align = (char *)((long)(buf + ALIGN_LEN) &
657                                                      ~ALIGN_LEN);
658                                 strncpy(buf_align, msg, bufsize);
659                         }
660                         while (len > 0) {
661                                 rc = write(fd, buf_align, len);
662                                 if (rc == -1) {
663                                         save_errno = errno;
664                                         perror("write");
665                                         exit(save_errno);
666                                 }
667                                 if (rc < len)
668                                         fprintf(stderr, "short write: %lld/%u\n",
669                                                 rc, len);
670                                 len -= rc;
671                         }
672                         break;
673                 case 'W':
674                         for (i = 0; i < mmap_len && mmap_ptr; i += 4096)
675                                 mmap_ptr[i] += junk++;
676                         break;
677                 case 'x': {
678                         __u64 dv;
679
680                         rc = llapi_get_data_version(fd, &dv, 0);
681                         if (rc) {
682                                 fprintf(stderr, "cannot get file data version"
683                                         " %lld\n", rc);
684                                 exit(-rc);
685                         }
686                         printf("dataversion is %ju\n", (uintmax_t)dv);
687                         break;
688                 }
689                 case 'X': {
690                         __u32 layout_version;
691
692                         rc = llapi_get_ost_layout_version(fd, &layout_version);
693                         if (rc) {
694                                 fprintf(stderr, "cannot get ost layout version"
695                                         " %lld\n", rc);
696                                 exit(-rc);
697                         }
698                         printf("ostlayoutversion: %u\n", layout_version);
699                         break;
700                 }
701                 case 'y':
702                         if (fsync(fd) == -1) {
703                                 save_errno = errno;
704                                 perror("fsync");
705                                 exit(save_errno);
706                         }
707                         break;
708                 case 'Y':
709                         if (fdatasync(fd) == -1) {
710                                 save_errno = errno;
711                                 perror("fdatasync");
712                                 exit(save_errno);
713                         }
714                         break;
715                 case 'z': {
716                         off_t off;
717
718                         len = atoi(commands + 1);
719                         off = lseek(fd, len, SEEK_SET);
720                         if (off == (off_t)-1) {
721                                 save_errno = errno;
722                                 perror("lseek");
723                                 exit(save_errno);
724                         }
725
726                         rc = off;
727                         break;
728                 }
729                 case 'Z': {
730                         off_t off;
731
732                         len = atoi(commands + 1);
733                         off = lseek(fd, len, SEEK_CUR);
734                         if (off == (off_t)-1) {
735                                 save_errno = errno;
736                                 perror("lseek");
737                                 exit(save_errno);
738                         }
739
740                         rc = off;
741                         break;
742                 }
743                 case '-':
744                 case '0':
745                 case '1':
746                 case '2':
747                 case '3':
748                 case '4':
749                 case '5':
750                 case '6':
751                 case '7':
752                 case '8':
753                 case '9':
754                         break;
755                 default:
756                         fprintf(stderr, "unknown command \"%c\"\n", *commands);
757                         fprintf(stderr, usage, argv[0]);
758                         exit(1);
759                 }
760         }
761
762         if (buf)
763                 free(buf);
764
765         return 0;
766 }