Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ DIALECT_NEUTRAL_TESTS = tests/case-00-hello.bash \
tests/case-20-exit-status.bash \
tests/case-20-fd-only-inclusion.bash \
tests/case-20-handle-missing-files.bash \
tests/case-20-soft-errors.bash \
tests/case-20-offset-field.bash \
tests/case-20-repeat-count.bash \
tests/case-21-exit-Q-status.bash \
Expand Down
7 changes: 7 additions & 0 deletions lib/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,13 @@ enum ExitStatus {
# define LSOF_SEARCH_FAILURE \
(FsearchErr ? LSOF_EXIT_ERROR : LSOF_EXIT_SUCCESS)

/*
* Argument helper results: 0 = success, 1 = hard error,
* SOFT_ERR = warning-level failure. Hard errors take precedence
* when combining results.
*/
enum { SOFT_ERR = 2 };

/*
* Structure definitions
*/
Expand Down
29 changes: 18 additions & 11 deletions src/arg.c
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ int enter_efsys(struct lsof_context *ctx, /* context */
(void)fprintf(stderr,
"%s: -e not followed by a file system path: \"%s\"\n",
Pn, e);
return (1);
return (SOFT_ERR);
}
if (!(ec = mkstrcpy(e, (MALLOC_S *)NULL))) {
(void)fprintf(stderr, "%s: no space for -e string: ", Pn);
Expand All @@ -609,7 +609,7 @@ int enter_efsys(struct lsof_context *ctx, /* context */
else {
if (!(path = Readlink(ctx, ec))) {
(void)free((FREE_P *)ec);
return (1);
return (SOFT_ERR);
}
if (Readlink_op == ec)
Readlink_op = (char *)NULL;
Expand Down Expand Up @@ -653,6 +653,7 @@ int enter_fd(struct lsof_context *ctx, /* context */
{
char c, *cp1, *cp2, *dash;
int err, excl, hi, lo;
int result, soft_err = 0;
char *fc;
/*
* Check for non-empty list and make a copy.
Expand Down Expand Up @@ -693,11 +694,17 @@ int enter_fd(struct lsof_context *ctx, /* context */
if (ckfd_range(ctx, cp1, dash, cp2, &lo, &hi))
err = 1;
else {
if (enter_fd_lst(ctx, (char *)NULL, lo, hi, excl))
result = enter_fd_lst(ctx, (char *)NULL, lo, hi, excl);
if (result == SOFT_ERR)
soft_err = 1;
else if (result)
err = 1;
}
} else {
if (enter_fd_lst(ctx, cp1, 0, 0, excl))
result = enter_fd_lst(ctx, cp1, 0, 0, excl);
if (result == SOFT_ERR)
soft_err = 1;
else if (result)
err = 1;
}
}
Expand All @@ -706,7 +713,7 @@ int enter_fd(struct lsof_context *ctx, /* context */
cp1 = cp2 + 1;
}
(void)free((FREE_P *)fc);
return (err);
return (err ? 1 : (soft_err ? SOFT_ERR : 0));
}

/*
Expand Down Expand Up @@ -749,7 +756,7 @@ static int enter_fd_lst(struct lsof_context *ctx, /* context */
excl ? "exclude" : "include",
FdlTy ? "exclude" : "include", buf);
}
return (1);
return (SOFT_ERR);
}
}
/*
Expand Down Expand Up @@ -884,10 +891,10 @@ int enter_dir(struct lsof_context *ctx, /* context */
if (!Fwarn)
(void)fprintf(stderr, "%s: +d not followed by a directory path\n",
Pn);
return (1);
return (SOFT_ERR);
}
if (!(dn = Readlink(ctx, d)))
return (1);
return (SOFT_ERR);
if (statsafely(ctx, dn, &sb)) {
if (!Fwarn) {
en = errno;
Expand All @@ -899,7 +906,7 @@ int enter_dir(struct lsof_context *ctx, /* context */
(void)free((FREE_P *)dn);
dn = (char *)NULL;
}
return (1);
return (SOFT_ERR);
}
if ((sb.st_mode & S_IFMT) != S_IFDIR) {
if (!Fwarn) {
Expand All @@ -910,7 +917,7 @@ int enter_dir(struct lsof_context *ctx, /* context */
(void)free((FREE_P *)dn);
dn = (char *)NULL;
}
return (1);
return (SOFT_ERR);
}

#if defined(HASSPECDEVD)
Expand Down Expand Up @@ -1135,7 +1142,7 @@ int enter_dir(struct lsof_context *ctx, /* context */
"%s: WARNING: no files found in directory: ", Pn);
safestrprt(d, stderr, 1);
}
return (1);
return (SOFT_ERR);
}
return (0);
}
Expand Down
33 changes: 29 additions & 4 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,10 @@ int main(int argc, char *argv[]) {
enum ExitStatus rv;
int gopt_rv;
int ad, c, i, n, se1, se2, ss;
int arg_result;
char *cp;
int err = 0;
int soft_err = 0;
enum ExitStatus ev = LSOF_EXIT_SUCCESS;
int fh = 0;
char *fmtr = (char *)NULL;
Expand Down Expand Up @@ -355,20 +357,29 @@ int main(int argc, char *argv[]) {
#endif /* defined(HASNCACHE) */
case 'd':
if (GOp == '+') {
if (enter_dir(ctx, GOv, 0))
arg_result = enter_dir(ctx, GOv, 0);
if (arg_result == SOFT_ERR)
soft_err = 1;
else if (arg_result)
err = 1;
else {
Selflags |= SELNM;
xover = 1;
}
} else {
if (enter_fd(ctx, GOv))
arg_result = enter_fd(ctx, GOv);
if (arg_result == SOFT_ERR)
soft_err = 1;
else if (arg_result)
err = 1;
}
break;
case 'D':
if (GOp == '+') {
if (enter_dir(ctx, GOv, 1))
arg_result = enter_dir(ctx, GOv, 1);
if (arg_result == SOFT_ERR)
soft_err = 1;
else if (arg_result)
err = 1;
else {
Selflags |= SELNM;
Expand All @@ -388,7 +399,10 @@ int main(int argc, char *argv[]) {

#if defined(HASEOPT)
case 'e':
if (enter_efsys(ctx, GOv, ((GOp == '+') ? 1 : 0)))
arg_result = enter_efsys(ctx, GOv, ((GOp == '+') ? 1 : 0));
if (arg_result == SOFT_ERR)
soft_err = 1;
else if (arg_result)
err = 1;
break;
#endif /* defined(HASEOPT) */
Expand Down Expand Up @@ -1181,8 +1195,19 @@ int main(int argc, char *argv[]) {
select_default_fields();
}

/*
* Preserve usage output for unsuppressed soft failures.
* Hard errors and explicit help requests always reach usage().
*/
if (soft_err && !Fwarn)
err = 1;
if (DChelp || err || Fhelp || fh || version)
usage(ctx, err ? 1 : 0, fh, version);
/*
* Suppressing warnings does not make the failed argument valid.
*/
if (soft_err)
Error(ctx);
/*
* Reduce the size of Suid[], if necessary.
*/
Expand Down
66 changes: 66 additions & 0 deletions tests/case-20-soft-errors.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/usr/bin/env bash
# Warning-level argument failures must not hide unrelated usage errors.
source tests/common.bash

work=$(mktemp -d "${TMPDIR:-/tmp}/lsof-soft-errors.XXXXXX") || exit 1
trap 'rm -rf "$work"' EXIT
touch "$work/file" || exit 1
missing=$work/missing

check_output() {
local expected=$1
shift
local output status expected_status=1
[[ $expected == help ]] && expected_status=0
output=$("$lsof" "$@" 2>&1)
status=$?
if [[ $status -ne $expected_status ]]; then
echo "unexpected exit status $status: $*"
return 1
fi
case $expected in
quiet)
[[ -z $output ]] && return 0
;;
warning)
[[ $output == *WARNING:* && $output == *usage:* ]] && return 0
;;
diagnostic)
[[ $output == *WARNING:* && $output != *usage:* ]] && return 0
;;
usage|help)
[[ $output == *usage:* ]] && return 0
;;
esac
echo "expected $expected output: $*"
echo "$output"
return 1
}

{
for option in +d +D; do
for path in "$missing" "$work/file"; do
check_output warning "$option" "$path" || exit 1
for flag in -w -t; do
check_output quiet "$flag" "$option" "$path" || exit 1
# A later flag suppresses usage, not an already printed warning.
check_output diagnostic "$option" "$path" "$flag" || exit 1
# Illegal FD ranges are portable hard argument errors.
check_output usage "$flag" -d 3-1 "$option" "$path" || exit 1
check_output usage "$flag" "$option" "$path" -d 3-1 || exit 1
check_output help "$flag" "$option" "$path" -h || exit 1
done
done
done
check_output quiet -w +d "$missing" +D "$missing" || exit 1
check_output usage -w +D "$missing" +w || exit 1
# Preserve soft results through enter_fd(), including range entries.
for list in '0,^1' '0,^1-2'; do
check_output quiet -w -d "$list" || exit 1
check_output usage -d "$list" || exit 1
done
# Hard errors must win in either order within one FD list.
check_output usage -w -d '0,^1,3-1' || exit 1
check_output usage -w -d '0,3-1,^1' || exit 1
echo "soft-error argument tests passed"
} > "$report" 2>&1
Loading