mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 15:32:35 +00:00
Print warning messages per default; new options -d0..-d4
This commit is contained in:
parent
dc777a00bb
commit
557f299b82
17 changed files with 115 additions and 62 deletions
6
CHANGES
6
CHANGES
|
@ -3,6 +3,12 @@ Features:
|
|||
Added the --experimental option that enables use of features that might
|
||||
change in the future.
|
||||
|
||||
Now warning messages are printed by default. If you want to see only
|
||||
errors and fatals as in previous versions, use option -d0;
|
||||
option -d4 is equivalent to -dddd and to -d -d -d -d
|
||||
The number of warnings has been reduced, e.g.removing a non existing
|
||||
file does in most cases no longer log a warning.
|
||||
|
||||
####################### V 1.7.4.5 (not released):
|
||||
|
||||
Corrections:
|
||||
|
|
|
@ -541,7 +541,7 @@ if test -n "$WITH_OPENSSL"; then
|
|||
[AC_TRY_COMPILE([#include <openssl/ssl.h>],[;],
|
||||
[sc_cv_have_openssl_ssl_h=yes; OPENSSL_BASE=""; ],
|
||||
[sc_cv_have_openssl_ssl_h=no
|
||||
if [ "$OPENSSL_BASE" ]; then
|
||||
if test "$OPENSSL_BASE"; then
|
||||
Ds="$OPENSSL_BASE"
|
||||
else
|
||||
Ds="/sw /usr/local /opt/freeware /usr/sfw /usr/local/ssl"
|
||||
|
|
15
doc/socat.yo
15
doc/socat.yo
|
@ -109,12 +109,15 @@ dit(bf(tt(-hh | -??)))
|
|||
dit(bf(tt(-hhh | -???)))
|
||||
Like -hh, plus a list of all available address option names.
|
||||
label(option_d)dit(bf(tt(-d)))
|
||||
Without this option, only fatal and error messages are generated; applying
|
||||
this option also prints warning messages. See link(DIAGNOSTICS)(DIAGNOSTICS)
|
||||
for more information.
|
||||
label(option_d_d)dit(bf(tt(-d -d))) Prints fatal, error, warning, and notice messages.
|
||||
dit(bf(tt(-d -d -d))) Prints fatal, error, warning, notice, and info messages.
|
||||
dit(bf(tt(-d -d -d -d))) Prints fatal, error, warning, notice, info, and debug
|
||||
Without this option, only fatal, error, and warning messages are printed;
|
||||
applying this option also prints notice messages.
|
||||
See link(DIAGNOSTICS)(DIAGNOSTICS) for more information.
|
||||
label(option_d0)dit(bf(tt(-d0)))
|
||||
With this option, only fatal and error messages are printed; this restores
|
||||
the behaviour of socat() up to version 1.7.4.
|
||||
label(option_d_d)dit(bf(tt(-d -d | -dd | -d2))) Prints fatal, error, warning, and notice messages.
|
||||
dit(bf(tt(-d -d -d | -ddd | -d3))) Prints fatal, error, warning, notice, and info messages.
|
||||
dit(bf(tt(-d -d -d -d | -dddd | -d4))) Prints fatal, error, warning, notice, info, and debug
|
||||
messages.
|
||||
dit(bf(tt(-D)))
|
||||
Logs information about file descriptors before starting the transfer phase.
|
||||
|
|
6
error.c
6
error.c
|
@ -45,7 +45,7 @@ static void _diag_exit(int status);
|
|||
|
||||
|
||||
struct diag_opts diagopts =
|
||||
{ NULL, E_ERROR, E_ERROR, 0, NULL, LOG_DAEMON, false, 0, false, NULL, true } ;
|
||||
{ NULL, E_WARN, E_ERROR, 0, NULL, LOG_DAEMON, false, 0, false, NULL, true } ;
|
||||
|
||||
static void msg2(
|
||||
#if HAVE_CLOCK_GETTIME
|
||||
|
@ -194,7 +194,6 @@ void diag_set(char what, const char *arg) {
|
|||
case 'p': diagopts.progname = arg;
|
||||
openlog(diagopts.progname, LOG_PID, diagopts.logfacility);
|
||||
break;
|
||||
case 'd': --diagopts.msglevel; break;
|
||||
case 'u': diagopts.micros = true; break;
|
||||
default: msg(E_ERROR, "unknown diagnostic option %c", what);
|
||||
}
|
||||
|
@ -206,6 +205,9 @@ void diag_set_int(char what, int arg) {
|
|||
case 'D': diagopts.msglevel = arg; break;
|
||||
case 'e': diagopts.exitlevel = arg; break;
|
||||
case 'x': diagopts.exitstatus = arg; break;
|
||||
case 'd':
|
||||
diagopts.msglevel = arg;
|
||||
break;
|
||||
case 'h': diagopts.withhostname = arg;
|
||||
if ((diagopts.hostname = getenv("HOSTNAME")) == NULL) {
|
||||
struct utsname ubuf;
|
||||
|
|
6
fdname.c
6
fdname.c
|
@ -99,8 +99,8 @@ static int procgetfdname(int fd, char *filepath, size_t pathsize) {
|
|||
#endif
|
||||
"/%d", pid, fd);
|
||||
if ((len = Readlink(procpath, filepath, pathsize-1)) < 0) {
|
||||
Warn4("readlink(\"%s\", %p, "F_Zu"): %s",
|
||||
procpath, filepath, pathsize, strerror(errno));
|
||||
Notice4("readlink(\"%s\", %p, "F_Zu"): %s",
|
||||
procpath, filepath, pathsize, strerror(errno));
|
||||
len = 0;
|
||||
}
|
||||
filepath[len] = '\0';
|
||||
|
@ -260,7 +260,7 @@ int sockname(int fd, FILE *outfile, char style) {
|
|||
rc = Getsockopt(fd, SOL_SOCKET, SO_PROTOTYPE, &proto, &optlen);
|
||||
#endif
|
||||
if (rc < 0) {
|
||||
Warn5("getsocktop(%d, SOL_SOCKET, "
|
||||
Notice5("getsocktop(%d, SOL_SOCKET, "
|
||||
#ifdef SO_PROTOCOL
|
||||
"SO_PROTOCOL"
|
||||
#else
|
||||
|
|
28
socat.c
28
socat.c
|
@ -93,6 +93,7 @@ int main(int argc, const char *argv[]) {
|
|||
double rto;
|
||||
int i, argc0, result;
|
||||
bool isdash = false;
|
||||
int msglevel = 0;
|
||||
struct utsname ubuf;
|
||||
int lockrc;
|
||||
|
||||
|
@ -124,10 +125,32 @@ int main(int argc, const char *argv[]) {
|
|||
Exit(0);
|
||||
#endif /* WITH_HELP */
|
||||
case 'd':
|
||||
a = *arg1+1;
|
||||
a = *arg1+2;
|
||||
switch (*a) {
|
||||
case 'd':
|
||||
break;
|
||||
case '-': case '0': case '1': case '2': case '3': case '4':
|
||||
{
|
||||
char *endptr;
|
||||
msglevel = strtol(a, &endptr, 0);
|
||||
if (endptr == a || *endptr) {
|
||||
Error2("Invalid (trailing) character(s) \"%c\" in \"%s\"option", *a, *arg1);
|
||||
}
|
||||
diag_set_int('d', 4-msglevel);
|
||||
}
|
||||
break;
|
||||
case '\0':
|
||||
++msglevel;
|
||||
diag_set_int('d', 4-msglevel);
|
||||
break;
|
||||
default: socat_usage(stderr);
|
||||
}
|
||||
if (*a != 'd') break;
|
||||
++msglevel;
|
||||
while (*a) {
|
||||
if (*a == 'd') {
|
||||
diag_set('d', NULL);
|
||||
++msglevel;
|
||||
diag_set_int('d', 4-msglevel);
|
||||
} else {
|
||||
socat_usage(stderr);
|
||||
Exit(1);
|
||||
|
@ -394,6 +417,7 @@ void socat_usage(FILE *fd) {
|
|||
fputs(" -hhh like -hh, plus a list of all available address option names\n", fd);
|
||||
#endif /* WITH_HELP */
|
||||
fputs(" -d[ddd] increase verbosity (use up to 4 times; 2 are recommended)\n", fd);
|
||||
fputs(" -d0|1|2|3|4 set verbosity level (0: Errors; 4 all including Debug)\n", fd);
|
||||
#if WITH_FILAN
|
||||
fputs(" -D analyze file descriptors before loop\n", fd);
|
||||
#endif
|
||||
|
|
18
test.sh
18
test.sh
|
@ -6545,7 +6545,7 @@ NAME=UNIEXECEOF
|
|||
case "$TESTS" in
|
||||
*%$N%*|*%functions%*|*%$NAME%*)
|
||||
TEST="$NAME: give exec'd write-only process a chance to flush (-u)"
|
||||
testod "$N" "$TEST" "" exec:"$OD_C" "$opts -u"
|
||||
testod "$N" "$TEST" "" EXEC:"$OD_C" "$opts -u"
|
||||
esac
|
||||
N=$((N+1))
|
||||
|
||||
|
@ -6554,7 +6554,7 @@ NAME=REVEXECEOF
|
|||
case "$TESTS" in
|
||||
*%$N%*|*%functions%*|*%$NAME%*)
|
||||
TEST="$NAME: give exec'd write-only process a chance to flush (-U)"
|
||||
testod "$N" "$TEST" exec:"$OD_C" "-" "$opts -U"
|
||||
testod "$N" "$TEST" EXEC:"$OD_C" "-" "$opts -U"
|
||||
esac
|
||||
N=$((N+1))
|
||||
|
||||
|
@ -12336,7 +12336,7 @@ if [ $RLIMIT_NOFILE -gt 1024 ]; then
|
|||
RLIMIT_NOFILE="$(ulimit -n)"
|
||||
fi
|
||||
newport tcp4
|
||||
CMD0="$TRACE $SOCAT $opts TCP-LISTEN:$PORT,$REUSEADDR,range=$LOCALHOST:255.255.255.255 PIPE"
|
||||
CMD0="$TRACE $SOCAT -d0 $opts TCP-LISTEN:$PORT,$REUSEADDR,range=$LOCALHOST:255.255.255.255 PIPE"
|
||||
CMD1="$TRACE $SOCAT $opts -t 0 /dev/null TCP:$SECONDADDR:$PORT,bind=$SECONDADDR"
|
||||
CMD2="$TRACE $SOCAT $opts - TCP:$LOCALHOST:$PORT,bind=$LOCALHOST"
|
||||
printf "test $F_n $TEST... " $N
|
||||
|
@ -12359,6 +12359,12 @@ if [ $rc2 -ne 0 ]; then
|
|||
listFAIL="$listFAIL $N"
|
||||
elif [ -f "$tdiff" -a ! -s "$tdiff" ]; then
|
||||
$PRINTF "$OK\n"
|
||||
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
|
||||
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
|
||||
if [ "$VERBOSE" ]; then echo "$CMD1"; fi
|
||||
if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
|
||||
if [ "$VERBOSE" ]; then echo "$CMD2"; fi
|
||||
if [ "$DEBUG" ]; then cat "${te}2" >&2; fi
|
||||
numOK=$((numOK+1))
|
||||
else
|
||||
$PRINTF "$FAILED\n"
|
||||
|
@ -15653,7 +15659,7 @@ N=$((N+1))
|
|||
NAME=INTEGER_GARBAGE
|
||||
case "$TESTS" in
|
||||
*%$N%*|*%functions%*|*%syntax%*|*%bugs%*|*%$NAME%*)
|
||||
TEST="$NAME: Error on trailing garbabe"
|
||||
TEST="$NAME: Error on trailing garbage"
|
||||
# Invoke Socat with pty and option ispeed=b19200.
|
||||
# When socat terminates with error the test succeeded
|
||||
if ! eval $NUMCOND; then :; else
|
||||
|
@ -15993,7 +15999,7 @@ tf="$td/test$N.stdout"
|
|||
te="$td/test$N.stderr"
|
||||
tdiff="$td/test$N.diff"
|
||||
da="test$N $(date) $RANDOM"
|
||||
CMD="$TRACE $SOCAT $opts - EXEC:\"$FILAN -s\",stderr"
|
||||
CMD="$TRACE $SOCAT $opts - EXEC:\"$FILAN -s\""
|
||||
printf "test $F_n $TEST... " $N
|
||||
eval "$CMD" >"${tf}" 2>"${te}"
|
||||
# "door" is a special FD type on Solaris/SunOS
|
||||
|
@ -16039,7 +16045,7 @@ tf="$td/test$N.stdout"
|
|||
te="$td/test$N.stderr"
|
||||
tdiff="$td/test$N.diff"
|
||||
da="test$N $(date) $RANDOM"
|
||||
CMD="$TRACE $SOCAT $opts -r $td/test$N.-r -R $td/test$N.-R - EXEC:\"$FILAN -s\",stderr"
|
||||
CMD="$TRACE $SOCAT $opts -r $td/test$N.-r -R $td/test$N.-R - EXEC:\"$FILAN -s\""
|
||||
printf "test $F_n $TEST... " $N
|
||||
eval "$CMD" >"${tf}" 2>"${te}"
|
||||
# "door" is a special FD type on Solaris/SunOS
|
||||
|
|
|
@ -306,7 +306,7 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl
|
|||
} while (true);
|
||||
applyopts_cloexec(ps, opts);
|
||||
if (Getpeername(ps, &pa->soa, &pas) < 0) {
|
||||
Warn4("getpeername(%d, %p, {"F_socklen"}): %s",
|
||||
Notice4("getpeername(%d, %p, {"F_socklen"}): %s",
|
||||
ps, pa, pas, strerror(errno));
|
||||
pa = NULL;
|
||||
}
|
||||
|
|
24
xio-named.c
24
xio-named.c
|
@ -59,9 +59,12 @@ int applyopts_named(const char *filename, struct opt *opts, unsigned int phase)
|
|||
break;
|
||||
case OPT_UNLINK_EARLY:
|
||||
case OPT_UNLINK:
|
||||
xio_unlink(filename, E_ERROR);
|
||||
break;
|
||||
case OPT_UNLINK_LATE:
|
||||
if (Unlink(filename) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
/* We have just created/opened it, that's - surprising! */
|
||||
Warn2("unlink(\"%s\"): %s", filename, strerror(errno));
|
||||
} else {
|
||||
Error2("unlink(\"%s\"): %s", filename, strerror(errno));
|
||||
|
@ -209,4 +212,25 @@ int _xioopen_open(const char *path, int rw, struct opt *opts) {
|
|||
return fd;
|
||||
}
|
||||
|
||||
/* Wrapper around Unlink() that handles the case of non existing file (ENOENT)
|
||||
just as E_INFO. All other errors are handled with level. */
|
||||
int xio_unlink(
|
||||
const char *filename, /* the file to be removed */
|
||||
int level) /* the severity level for other errors, e.g.E_ERROR */
|
||||
{
|
||||
int _errno;
|
||||
|
||||
if (Unlink(filename) < 0) {
|
||||
_errno = errno;
|
||||
if (errno == ENOENT) {
|
||||
Info2("unlink(\"%s\"): %s", filename, strerror(errno));
|
||||
} else {
|
||||
Msg2(level, "unlink(\"%s\"): %s", filename, strerror(errno));
|
||||
errno = _errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _WITH_NAMED */
|
||||
|
|
|
@ -23,4 +23,6 @@ extern int _xioopen_named_early(int argc, const char *argv[], xiofile_t *xfd,
|
|||
bool *exists, struct opt *opts);
|
||||
extern int _xioopen_open(const char *path, int rw, struct opt *opts);
|
||||
|
||||
extern int xio_unlink(const char *pathname, int level);
|
||||
|
||||
#endif /* !defined(__xio_named_h_included) */
|
||||
|
|
|
@ -105,12 +105,7 @@ static int xioopen_fifo(int argc, const char *argv[], struct opt *opts, int xiof
|
|||
|
||||
if (opt_unlink_early) {
|
||||
if (Unlink(pipename) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
Warn2("unlink(%s): %s", pipename, strerror(errno));
|
||||
} else {
|
||||
Error2("unlink(%s): %s", pipename, strerror(errno));
|
||||
return STAT_RETRYLATER;
|
||||
}
|
||||
return STAT_RETRYLATER;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -149,9 +149,7 @@ static int xioopen_pty(int argc, const char *argv[], struct opt *opts, int xiofl
|
|||
#endif /* HAVE_OPENPTY */
|
||||
|
||||
if (!retropt_string(opts, OPT_SYMBOLIC_LINK, &linkname)) {
|
||||
if (Unlink(linkname) < 0 && errno != ENOENT) {
|
||||
Error2("unlink(\"%s\"): %s", linkname, strerror(errno));
|
||||
}
|
||||
xio_unlink(linkname, E_ERROR);
|
||||
if (Symlink(ptyname, linkname) < 0) {
|
||||
Error3("symlink(\"%s\", \"%s\"): %s",
|
||||
ptyname, linkname, strerror(errno));
|
||||
|
|
|
@ -1132,8 +1132,10 @@ void xiosigaction_hasread(int signum
|
|||
diag_in_handler = 0;
|
||||
errno = _errno;
|
||||
return;
|
||||
} else if (pid < 0 && errno == EINTR) {
|
||||
Info1("xiosigaction_hasread(): %s", strerror(errno));
|
||||
} else if (pid < 0 && errno == ECHILD) {
|
||||
Msg(wassig?E_INFO:E_WARN,
|
||||
Msg(wassig?E_INFO:E_NOTICE,
|
||||
"waitpid(-1, {}, WNOHANG): "F_strerror);
|
||||
Info("xiosigaction_hasread() finished");
|
||||
Debug("xiosigaction_hasread() ->");
|
||||
|
|
33
xio-unix.c
33
xio-unix.c
|
@ -157,13 +157,7 @@ static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, i
|
|||
|
||||
if (!(ABSTRACT && abstract)) {
|
||||
if (opt_unlink_early) {
|
||||
if (Unlink(name) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
Warn2("unlink(\"%s\"): %s", name, strerror(errno));
|
||||
} else {
|
||||
Error2("unlink(\"%s\"): %s", name, strerror(errno));
|
||||
}
|
||||
}
|
||||
xio_unlink(name, E_ERROR);
|
||||
} else {
|
||||
struct stat buf;
|
||||
if (Lstat(name, &buf) == 0) {
|
||||
|
@ -463,13 +457,7 @@ int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts,
|
|||
|
||||
if (!(ABSTRACT && abstract)) {
|
||||
if (opt_unlink_early) {
|
||||
if (Unlink(name) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
Warn2("unlink(\"%s\"): %s", name, strerror(errno));
|
||||
} else {
|
||||
Error2("unlink(\"%s\"): %s", name, strerror(errno));
|
||||
}
|
||||
}
|
||||
xio_unlink(name, E_ERROR);
|
||||
} else {
|
||||
struct stat buf;
|
||||
if (Lstat(name, &buf) == 0) {
|
||||
|
@ -550,13 +538,7 @@ int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts,
|
|||
|
||||
if (!(ABSTRACT && abstract)) {
|
||||
if (opt_unlink_early) {
|
||||
if (Unlink(name) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
Warn2("unlink(\"%s\"): %s", name, strerror(errno));
|
||||
} else {
|
||||
Error2("unlink(\"%s\"): %s", name, strerror(errno));
|
||||
}
|
||||
}
|
||||
xio_unlink(name, E_ERROR);
|
||||
} else {
|
||||
struct stat buf;
|
||||
if (Lstat(name, &buf) == 0) {
|
||||
|
@ -668,7 +650,7 @@ _xioopen_unix_client(xiosingle_t *xfd, int xioflags, unsigned groups,
|
|||
if (errno != EPROTOTYPE || socktype != 0)
|
||||
break;
|
||||
if (needbind)
|
||||
Unlink(us.un.sun_path);
|
||||
xio_unlink(us.un.sun_path, E_ERROR);
|
||||
dropopts2(opts, PH_INIT, PH_SPEC); opts = opts0;
|
||||
|
||||
socktype = SOCK_SEQPACKET;
|
||||
|
@ -682,7 +664,7 @@ _xioopen_unix_client(xiosingle_t *xfd, int xioflags, unsigned groups,
|
|||
if (errno != EPROTOTYPE && errno != EPROTONOSUPPORT/*AIX*/)
|
||||
break;
|
||||
if (needbind)
|
||||
Unlink(us.un.sun_path);
|
||||
xio_unlink(us.un.sun_path, E_ERROR);
|
||||
dropopts2(opts, PH_INIT, PH_SPEC); opts = opts0;
|
||||
|
||||
xfd->peersa = them;
|
||||
|
@ -699,9 +681,8 @@ _xioopen_unix_client(xiosingle_t *xfd, int xioflags, unsigned groups,
|
|||
|
||||
if (result != 0) {
|
||||
Error2("UNIX-CLIENT:%s: %s", name, strerror(errno));
|
||||
if (needbind) {
|
||||
Unlink(us.un.sun_path);
|
||||
}
|
||||
if (needbind)
|
||||
xio_unlink(us.un.sun_path, E_ERROR);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ int xioclose1(struct single *pipe) {
|
|||
}
|
||||
if (pipe->opt_unlink_close && pipe->unlink_close) {
|
||||
if (Unlink(pipe->unlink_close) < 0) {
|
||||
Info2("unlink(\"%s\"): %s", pipe->unlink_close, strerror(errno));
|
||||
Warn2("unlink(\"%s\"): %s", pipe->unlink_close, strerror(errno));
|
||||
}
|
||||
free(pipe->unlink_close);
|
||||
}
|
||||
|
|
|
@ -112,6 +112,9 @@ int xioshutdown(xiofile_t *sock, int how) {
|
|||
sock->stream.fd, how, strerror(errno));
|
||||
}
|
||||
if ((sock->stream.flags&XIO_ACCMODE) == XIO_WRONLY) {
|
||||
pid_t pid;
|
||||
int level;
|
||||
|
||||
/* the child process might want to flush some data before terminating
|
||||
*/
|
||||
int status = 0;
|
||||
|
@ -133,9 +136,14 @@ int xioshutdown(xiofile_t *sock, int how) {
|
|||
#else
|
||||
Alarm(1 /*! sock->stream.para.exec.waitdie */);
|
||||
#endif /* !HAVE_SETITIMER */
|
||||
if (Waitpid(sock->stream.para.exec.pid, &status, 0) < 0) {
|
||||
Warn3("waitpid("F_pid", %p, 0): %s",
|
||||
sock->stream.para.exec.pid, &status, strerror(errno));
|
||||
pid = Waitpid(sock->stream.para.exec.pid, &status, 0);
|
||||
if (pid < 0) {
|
||||
if (errno == EINTR)
|
||||
level = E_INFO;
|
||||
else
|
||||
level = E_WARN;
|
||||
Msg3(level, "waitpid("F_pid", %p, 0): %s",
|
||||
sock->stream.para.exec.pid, &status, strerror(errno));
|
||||
}
|
||||
Alarm(0);
|
||||
}
|
||||
|
|
|
@ -92,8 +92,10 @@ void childdied(int signum) {
|
|||
diag_in_handler = 0;
|
||||
errno = _errno;
|
||||
return;
|
||||
} else if (pid < 0 && errno == EINTR) {
|
||||
Info1("childdied(): %s", strerror(errno));
|
||||
} else if (pid < 0 && errno == ECHILD) {
|
||||
Msg(wassig?E_INFO:E_WARN,
|
||||
Msg(wassig?E_INFO:E_NOTICE,
|
||||
"waitpid(-1, {}, WNOHANG): "F_strerror);
|
||||
Info("childdied() finished");
|
||||
diag_in_handler = 0;
|
||||
|
|
Loading…
Reference in a new issue