diff options
Diffstat (limited to 'testing/sgs_dd.c')
-rw-r--r-- | testing/sgs_dd.c | 415 |
1 files changed, 334 insertions, 81 deletions
diff --git a/testing/sgs_dd.c b/testing/sgs_dd.c index 2d4cbe17..6642ac1f 100644 --- a/testing/sgs_dd.c +++ b/testing/sgs_dd.c @@ -1,33 +1,15 @@ -/* We need F_SETSIG, (signal redirect), so following define */ -#define _GNU_SOURCE 1 - -#include <unistd.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdbool.h> -#include <stdarg.h> -#include <string.h> -#include <ctype.h> -#include <errno.h> -#include <poll.h> -#include <signal.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include "sg_lib.h" -#include "sg_linux_inc.h" -#include "sg_io_linux.h" - -/* Test code for the extensions to the Linux OS SCSI generic ("sg") +/* + * Test code for the extensions to the Linux OS SCSI generic ("sg") * device driver. * Copyright (C) 1999-2018 D. Gilbert and P. Allworth + * * 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, or (at your option) * any later version. * + * SPDX-License-Identifier: GPL-2.0-or-later + * * This program is a specialization of the Unix "dd" command in which * one or both of the given files is a scsi generic device. A block size * ('bs') is assumed to be 512 if not given. This program complains if @@ -51,8 +33,52 @@ * chosen. SIGIO is a synonym for SIGPOLL; SIGIO seems to be deprecated. */ +/* We need F_SETSIG, (signal redirect), so following define */ +#define _GNU_SOURCE 1 + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <poll.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifndef HAVE_LINUX_SG_V4_HDR +/* Kernel uapi header contain __user decorations on user space pointers + * to indicate they are unsafe in the kernel space. However glibc takes + * all those __user decorations out from headers in /usr/include/linux . + * So to stop compile errors when directly importing include/uapi/scsi/sg.h + * undef __user before doing that include. */ +#define __user -static const char * version_str = "4.01 20181223"; +/* Want to block the original sg.h header from also being included. That + * causes lots of multiple definition errors. This will only work if this + * header is included _before_ the original sg.h header. */ +#define _SCSI_GENERIC_H /* original kernel header guard */ +#define _SCSI_SG_H /* glibc header guard */ + +#include "uapi_sg.h" /* local copy of include/uapi/scsi/sg.h */ + +#else +#define __user +#endif /* end of: ifndef HAVE_LINUX_SG_V4_HDR */ + +#include "sg_lib.h" +#include "sg_linux_inc.h" +#include "sg_io_linux.h" +#include "sg_pr2serr.h" +#include "sg_unaligned.h" + + +static const char * version_str = "4.03 20190105"; static const char * my_name = "sgs_dd"; #define DEF_BLOCK_SIZE 512 @@ -83,6 +109,15 @@ static const char * my_name = "sgs_dd"; #define INOUTF_SZ 512 #define EBUFF_SZ 512 +struct flags_t { + bool dio; + bool excl; + bool immed; + bool mmap; + bool noxfer; + bool v3; + bool v4; +}; typedef struct request_element { @@ -93,7 +128,11 @@ typedef struct request_element int blk; int num_blks; uint8_t * buffp; + uint8_t * free_buffp; sg_io_hdr_t io_hdr; + struct sg_io_v4 io_v4; + struct flags_t * iflagp; + struct flags_t * oflagp; uint8_t cmd[S_RW_LEN]; uint8_t sb[SENSE_BUFF_LEN]; int result; @@ -103,7 +142,6 @@ typedef struct request_collection { bool in_is_sg; bool out_is_sg; - bool dio; bool use_rt_sig; int infd; int in_blk; /* most recent read */ @@ -127,30 +165,35 @@ typedef struct request_collection int sigs_io_received; Rq_elem * rd_posp; Rq_elem * wr_posp; + struct flags_t iflag; + struct flags_t oflag; Rq_elem elem[SGQ_NUM_ELEMS]; } Rq_coll; +static bool sgs_old_sg_driver = false; + static void usage(void) { printf("Usage: " - "sgs_dd [if=<ifile>] [skip=<n>] [of=<ofile>] [seek=<n>]\n" - " [bs=<num>] [bpt=<num>] [count=<n>]" - " [deb=<n>] [dio=0|1]\n" - " [rt_sig=0|1] [--version]\n" + "sgs_dd [bpt=BPT] [bs=BS] [count=NUM] [deb=DEB] [if=IFILE]\n" + " [iflag=FLAGS] [of=OFILE] [oflag=FLAGS] " + "[rt_sig=0|1]\n" + " [seek=SEEK] [skip=SKIP] [--version]\n" "where:\n" " bpt blocks_per_transfer (default: 65536/bs (or 128 for " "bs=512))\n" - " bs not just any block size, the logical block size of " - "device\n" - " dio direct IO, 1->attempt, 0->indirect IO (def)\n" - " rt_sig 0->use SIGIO (def); 1->use RT sig (SIGRTMIN + 1)\n" + " bs must be the logical block size of device (def: 512)\n" " deb debug: 0->no debug (def); > 0 -> more debug\n" + " iflag comma separated list from: dio,excl,immed,mmap,noxfer," + "null,v3,v4\n" + " oflag same flags as iflag but bound to OFILE\n" + " rt_sig 0->use SIGIO (def); 1->use RT sig (SIGRTMIN + 1)\n" " <other operands> as per dd command\n\n"); printf("dd clone for testing Linux sg driver SIGPOLL and friends. Either " - "'if' or 'of'\nmust be a scsi generic device. If 'of' not given " - "then /dev/null assumed.\n"); + "IFILE or\nOFILE must be a scsi generic device. If OFILE not given " + "then /dev/null\nassumed.\n"); } /* Return of 0 -> success, -1 -> failure, 2 -> try again */ @@ -200,17 +243,19 @@ read_capacity(int sg_fd, int * num_sect, int * sect_sz) static int sg_start_io(Rq_coll * clp, Rq_elem * rep) { - sg_io_hdr_t * hp = &rep->io_hdr; + int fd = rep->wr ? clp->outfd : clp->infd; + struct flags_t * flagp = rep->wr ? rep->oflagp : rep->iflagp; int res; + sg_io_hdr_t * hp = &rep->io_hdr; + struct sg_io_v4 * h4p = &rep->io_v4; memset(rep->cmd, 0, sizeof(rep->cmd)); rep->cmd[0] = rep->wr ? 0x2a : 0x28; - rep->cmd[2] = (uint8_t)((rep->blk >> 24) & 0xFF); - rep->cmd[3] = (uint8_t)((rep->blk >> 16) & 0xFF); - rep->cmd[4] = (uint8_t)((rep->blk >> 8) & 0xFF); - rep->cmd[5] = (uint8_t)(rep->blk & 0xFF); - rep->cmd[7] = (uint8_t)((rep->num_blks >> 8) & 0xff); - rep->cmd[8] = (uint8_t)(rep->num_blks & 0xff); + sg_put_unaligned_be32((uint32_t)rep->blk, rep->cmd + 2); + sg_put_unaligned_be16((uint16_t)rep->num_blks, rep->cmd + 7); + if (flagp->v4) + goto do_v4; + memset(hp, 0, sizeof(sg_io_hdr_t)); hp->interface_id = 'S'; hp->cmd_len = sizeof(rep->cmd); @@ -223,8 +268,14 @@ sg_start_io(Rq_coll * clp, Rq_elem * rep) hp->timeout = DEF_TIMEOUT; hp->usr_ptr = rep; hp->pack_id = rep->blk; - if (clp->dio) + if (flagp->dio) hp->flags |= SG_FLAG_DIRECT_IO; + if (flagp->noxfer) + hp->flags |= SG_FLAG_NO_DXFER; + if (flagp->immed) + hp->flags |= SGV4_FLAG_IMMED; + if (flagp->mmap) + hp->flags |= SG_FLAG_MMAP_IO; #ifdef SG_DEBUG fprintf(stderr, "%s: SCSI %s, blk=%d num_blks=%d\n", __func__, rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks); @@ -233,8 +284,8 @@ sg_start_io(Rq_coll * clp, Rq_elem * rep) hp->dxfer_direction, hp->dxfer_len, hp->dxferp, hp->cmd_len); #endif - while (((res = write(rep->wr ? clp->outfd : clp->infd, hp, - sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) + while (((res = write(fd, hp, sizeof(sg_io_hdr_t))) < 0) && + (EINTR == errno)) ; if (res < 0) { if (ENOMEM == errno) @@ -251,22 +302,73 @@ sg_start_io(Rq_coll * clp, Rq_elem * rep) rep->state = SGQ_IO_STARTED; clp->sigs_waiting++; return 0; +do_v4: + memset(h4p, 0, sizeof(struct sg_io_v4)); + h4p->guard = 'Q'; + h4p->request_len = sizeof(rep->cmd); + h4p->request = (uint64_t)rep->cmd; + if (rep->wr) { + h4p->dout_xfer_len = clp->bs * rep->num_blks; + h4p->dout_xferp = (uint64_t)rep->buffp; + } else if (rep->num_blks > 0) { + h4p->din_xfer_len = clp->bs * rep->num_blks; + h4p->din_xferp = (uint64_t)rep->buffp; + } + h4p->max_response_len = sizeof(rep->sb); + h4p->response = (uint64_t)rep->sb; + h4p->timeout = DEF_TIMEOUT; + h4p->usr_ptr = (uint64_t)rep; + h4p->request_extra = rep->blk;/* N.B. blk --> pack_id --> request_extra */ + if (flagp->dio) + h4p->flags |= SG_FLAG_DIRECT_IO; + if (flagp->noxfer) + h4p->flags |= SG_FLAG_NO_DXFER; + if (flagp->immed) + h4p->flags |= SGV4_FLAG_IMMED; + if (flagp->mmap) + h4p->flags |= SG_FLAG_MMAP_IO; + while (((res = ioctl(fd, SG_IOSUBMIT, h4p)) < 0) && (EINTR == errno)) + ; + if (res < 0) { + if (ENOMEM == errno) + return 1; + if ((EDOM == errno) || (EAGAIN == errno)) { + rep->state = SGQ_IO_WAIT; /* busy so wait */ + return 0; + } + fprintf(stderr, "%s: ioctl(SG_IOSUBMIT): %s [%d]\n", __func__, + strerror(errno), errno); + rep->state = SGQ_IO_ERR; + return res; + } + rep->state = SGQ_IO_STARTED; + clp->sigs_waiting++; + return 0; } /* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */ static int sg_finish_io(Rq_coll * clp, bool wr, Rq_elem ** repp) { + bool dio = false; + bool is_v4 = wr ? clp->oflag.v4 : clp->iflag.v4; + int fd = wr ? clp->outfd : clp->infd; int res; sg_io_hdr_t io_hdr; sg_io_hdr_t * hp; + struct sg_io_v4 io_v4; + struct sg_io_v4 * h4p; Rq_elem * rep; + if (is_v4) + goto do_v4; memset(&io_hdr, 0 , sizeof(sg_io_hdr_t)); - while (((res = read(wr ? clp->outfd : clp->infd, &io_hdr, - sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) + while (((res = read(fd, &io_hdr, sizeof(sg_io_hdr_t))) < 0) && + (EINTR == errno)) ; rep = (Rq_elem *)io_hdr.usr_ptr; + if (rep) + dio = rep->wr ? clp->oflag.dio : clp->iflag.dio; if (res < 0) { fprintf(stderr, "%s: read(): %s [%d]\n", __func__, strerror(errno), errno); @@ -299,8 +401,7 @@ sg_finish_io(Rq_coll * clp, bool wr, Rq_elem ** repp) rep->state = SGQ_IO_ERR; return -1; } - if (clp->dio && - ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) + if (dio && ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) ++clp->dio_incomplete; /* count dios done as indirect IO */ clp->sum_of_resids += hp->resid; rep->state = SGQ_IO_FINISHED; @@ -309,6 +410,62 @@ sg_finish_io(Rq_coll * clp, bool wr, Rq_elem ** repp) fprintf(stderr, " SGQ_IO_FINISHED elem idx=%zd\n", rep - clp->elem); #endif return 0; +do_v4: + memset(&io_v4, 0 , sizeof(io_v4)); + io_v4.guard = 'Q'; + while (((res = ioctl(fd, SG_IORECEIVE, &io_v4)) < 0) && (EINTR == errno)) + ; + rep = (Rq_elem *)io_v4.usr_ptr; + if (res < 0) { + fprintf(stderr, "%s: ioctl(SG_IORECEIVE): %s [%d]\n", __func__, + strerror(errno), + errno); + if (rep) + rep->state = SGQ_IO_ERR; + return -1; + } + if (! (rep && (SGQ_IO_STARTED == rep->state))) { + fprintf(stderr, "%s: bad usr_ptr=0x%p\n", __func__, rep); + if (rep) + rep->state = SGQ_IO_ERR; + return -1; + } + memcpy(&rep->io_v4, &io_v4, sizeof(struct sg_io_v4)); + h4p = &rep->io_v4; + if (repp) + *repp = rep; + + res = sg_err_category_new(h4p->device_status, h4p->transport_status, + h4p->driver_status, + (const uint8_t *)h4p->response, + h4p->response_len); + switch (res) { + case SG_LIB_CAT_CLEAN: + break; + case SG_LIB_CAT_RECOVERED: + fprintf(stderr, "Recovered error on block=%d, num=%d\n", + rep->blk, rep->num_blks); + break; + case SG_LIB_CAT_UNIT_ATTENTION: + return 1; + default: + sg_linux_sense_print(rep->wr ? "writing": "reading", + h4p->device_status, h4p->transport_status, + h4p->driver_status, + (const uint8_t *)h4p->response, + h4p->response_len, true); + rep->state = SGQ_IO_ERR; + return -1; + } + if (dio && ((h4p->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) + ++clp->dio_incomplete; /* count dios done as indirect IO */ + clp->sum_of_resids += h4p->din_resid; + rep->state = SGQ_IO_FINISHED; +#ifdef SG_DEBUG + fprintf(stderr, "%s: %s ", __func__, wr ? "writing" : "reading"); + fprintf(stderr, " SGQ_IO_FINISHED elem idx=%zd\n", rep - clp->elem); +#endif + return 0; } static int @@ -318,9 +475,13 @@ sz_reserve(int fd, int bs, int bpt, bool rt_sig) res = ioctl(fd, SG_GET_VERSION_NUM, &t); if ((res < 0) || (t < 30000)) { - fprintf(stderr, "sgs_dd: sg driver prior to 3.x.y\n"); + fprintf(stderr, "sgs_dd: sg driver prior to 3.0.00\n"); return 1; } + else if (t < 40000) { + fprintf(stderr, "sgs_dd: warning: sg driver prior to 4.0.00\n"); + sgs_old_sg_driver = true; + } res = 0; t = bs * bpt; res = ioctl(fd, SG_SET_RESERVED_SIZE, &t); @@ -342,10 +503,11 @@ sz_reserve(int fd, int bs, int bpt, bool rt_sig) return 0; } -static void +static int init_elems(Rq_coll * clp) { Rq_elem * rep; + int res = 0; int k; clp->wr_posp = &clp->elem[0]; /* making ring buffer */ @@ -356,8 +518,28 @@ init_elems(Rq_coll * clp) for (k = 0; k < SGQ_NUM_ELEMS; ++k) { rep = &clp->elem[k]; rep->state = SGQ_FREE; - if (NULL == (rep->buffp = malloc(clp->bpt * clp->bs))) + rep->iflagp = &clp->iflag; + rep->oflagp = &clp->oflag; + rep->buffp = sg_memalign(clp->bpt * clp->bs, 0, &rep->free_buffp, + false); + if (NULL == rep->buffp) { fprintf(stderr, "out of memory creating user buffers\n"); + res = -ENOMEM; + } + } + return res; +} + +static void +remove_elems(Rq_coll * clp) +{ + Rq_elem * rep; + int k; + + for (k = 0; k < SGQ_NUM_ELEMS; ++k) { + rep = &clp->elem[k]; + if (rep->free_buffp) + free(rep->free_buffp); } } @@ -678,6 +860,49 @@ can_read_write(Rq_coll * clp) return SGQ_CAN_DO_NOTHING; } +static bool +process_flags(const char * arg, struct flags_t * fp) +{ + char buff[256]; + char * cp; + char * np; + + strncpy(buff, arg, sizeof(buff)); + buff[sizeof(buff) - 1] = '\0'; + if ('\0' == buff[0]) { + pr2serr("no flag found\n"); + return false; + } + cp = buff; + do { + np = strchr(cp, ','); + if (np) + *np++ = '\0'; + if (0 == strcmp(cp, "dio")) + fp->dio = true; + else if (0 == strcmp(cp, "excl")) + fp->excl = true; + else if (0 == strcmp(cp, "immed")) + fp->immed = true; + else if (0 == strcmp(cp, "mmap")) + fp->mmap = true; + else if (0 == strcmp(cp, "noxfer")) + fp->noxfer = true; + else if (0 == strcmp(cp, "null")) + ; + else if (0 == strcmp(cp, "v3")) + fp->v3 = true; + else if (0 == strcmp(cp, "v4")) + fp->v4 = true; + else { + pr2serr("unrecognised flag: %s\n", cp); + return false; + } + cp = np; + } while (cp); + return true; +} + int main(int argc, char * argv[]) @@ -688,15 +913,14 @@ main(int argc, char * argv[]) int ibs = 0; int obs = 0; int count = -1; + int in_num_sect = 0; + int out_num_sect = 0; + int res, k, in_sect_sz, out_sect_sz, crw, open_fl; char str[STR_SZ]; char * key; char * buf; char inf[INOUTF_SZ]; char outf[INOUTF_SZ]; - int res, k; - int in_num_sect = 0; - int out_num_sect = 0; - int in_sect_sz, out_sect_sz, crw; char ebuff[EBUFF_SZ]; Rq_coll rcoll; Rq_coll * clp = &rcoll; @@ -721,30 +945,38 @@ main(int argc, char * argv[]) buf++; if (*buf) *buf++ = '\0'; - if (strcmp(key,"if") == 0) - strncpy(inf, buf, INOUTF_SZ); - else if (strcmp(key,"of") == 0) - strncpy(outf, buf, INOUTF_SZ); - else if (0 == strcmp(key,"ibs")) - ibs = sg_get_num(buf); - else if (0 == strcmp(key,"obs")) - obs = sg_get_num(buf); + if (0 == strcmp(key,"bpt")) + clp->bpt = sg_get_num(buf); else if (0 == strcmp(key,"bs")) clp->bs = sg_get_num(buf); - else if (0 == strcmp(key,"bpt")) - clp->bpt = sg_get_num(buf); - else if (0 == strcmp(key,"skip")) - skip = sg_get_num(buf); - else if (0 == strcmp(key,"seek")) - seek = sg_get_num(buf); else if (0 == strcmp(key,"count")) count = sg_get_num(buf); - else if (0 == strcmp(key,"dio")) - clp->dio = !!sg_get_num(buf); - else if (0 == strcmp(key,"rt_sig")) - clp->use_rt_sig = !!sg_get_num(buf); else if (0 == strcmp(key,"deb")) clp->debug = sg_get_num(buf); + else if (0 == strcmp(key,"ibs")) + ibs = sg_get_num(buf); + else if (strcmp(key,"if") == 0) + strncpy(inf, buf, INOUTF_SZ); + else if (0 == strcmp(key, "iflag")) { + if (! process_flags(buf, &clp->iflag)) { + pr2serr("%sbad argument to 'iflag='\n", my_name); + return SG_LIB_SYNTAX_ERROR; + } + } else if (0 == strcmp(key,"obs")) + obs = sg_get_num(buf); + else if (strcmp(key,"of") == 0) + strncpy(outf, buf, INOUTF_SZ); + else if (0 == strcmp(key, "oflag")) { + if (! process_flags(buf, &clp->oflag)) { + pr2serr("%sbad argument to 'oflag='\n", my_name); + return SG_LIB_SYNTAX_ERROR; + } + } else if (0 == strcmp(key,"rt_sig")) + clp->use_rt_sig = !!sg_get_num(buf); + else if (0 == strcmp(key,"seek")) + seek = sg_get_num(buf); + else if (0 == strcmp(key,"skip")) + skip = sg_get_num(buf); else if ((0 == strcmp(key,"-V")) || (0 == strcmp(key,"--version"))) { fprintf(stderr, "%s: version: %s\n", my_name, version_str); return 0; @@ -793,7 +1025,8 @@ main(int argc, char * argv[]) clp->infd = STDIN_FILENO; clp->outfd = STDOUT_FILENO; if (inf[0] && ('-' != inf[0])) { - if ((clp->infd = open(inf, O_RDONLY)) < 0) { + open_fl = clp->iflag.excl ? O_EXCL : 0; + if ((clp->infd = open(inf, open_fl | O_RDONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, "sgs_dd: could not open %s for reading", inf); perror(ebuff); @@ -815,7 +1048,9 @@ main(int argc, char * argv[]) } else { /* looks like sg device so close then re-open it RW */ close(clp->infd); - if ((clp->infd = open(inf, O_RDWR | O_NONBLOCK)) < 0) { + open_fl = clp->iflag.excl ? O_EXCL : 0; + open_fl |= (O_RDWR | O_NONBLOCK); + if ((clp->infd = open(inf, open_fl)) < 0) { fprintf(stderr, "If %s is a sg device, need read+write " "permissions, even to read it!\n", inf); return 1; @@ -823,10 +1058,17 @@ main(int argc, char * argv[]) clp->in_is_sg = true; if (sz_reserve(clp->infd, clp->bs, clp->bpt, clp->use_rt_sig)) return 1; + if (sgs_old_sg_driver && (clp->iflag.v4 || clp->oflag.v4)) { + pr2serr("Unable to implement v4 flag because sg driver too " + "old\n"); + return 1; + } } } if (outf[0] && ('-' != outf[0])) { - if ((clp->outfd = open(outf, O_RDWR | O_NONBLOCK)) >= 0) { + open_fl = clp->oflag.excl ? O_EXCL : 0; + open_fl |= (O_RDWR | O_NONBLOCK); + if ((clp->outfd = open(outf, open_fl)) >= 0) { if (ioctl(clp->outfd, SG_GET_TIMEOUT, 0) < 0) { /* not a scsi generic device so now try and open RDONLY */ close(clp->outfd); @@ -836,10 +1078,19 @@ main(int argc, char * argv[]) if (sz_reserve(clp->outfd, clp->bs, clp->bpt, clp->use_rt_sig)) return 1; + if (sgs_old_sg_driver && (clp->iflag.v4 || clp->oflag.v4)) { + pr2serr("Unable to implement v4 flag because sg driver " + "too old\n"); + return 1; + } } } if (! clp->out_is_sg) { - if ((clp->outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) { + if (clp->outfd >= 0) + close(clp->outfd); + open_fl = clp->oflag.excl ? O_EXCL : 0; + open_fl |= (O_WRONLY | O_CREAT); + if ((clp->outfd = open(outf, open_fl, 0666)) < 0) { snprintf(ebuff, EBUFF_SZ, "sgs_dd: could not open %s for writing", outf); perror(ebuff); @@ -937,7 +1188,8 @@ main(int argc, char * argv[]) clp->out_count = count; clp->out_done_count = count; clp->out_blk = seek; - init_elems(clp); + res = init_elems(clp); + res = 0; /* vvvvvvvvvvvvvvvvv Main Loop vvvvvvvvvvvvvvvvvvvvvvvv */ while (clp->out_done_count > 0) { @@ -967,7 +1219,7 @@ main(int argc, char * argv[]) if (0 != clp->out_count) { fprintf(stderr, "Some error occurred, remaining blocks=%d\n", clp->out_count); - return 1; + res = 1; } fprintf(stderr, "%d+%d records in\n", count - clp->in_done_count, clp->in_partial); @@ -982,5 +1234,6 @@ main(int argc, char * argv[]) if (clp->debug > 0) fprintf(stderr, "SIGIO/SIGPOLL signals received: %d, RT sigs: %d\n", clp->sigs_io_received, clp->sigs_rt_received); - return 0; + remove_elems(clp); + return res; } |