Skip to content

Commit 92f62a1

Browse files
joakimeolamy
andauthored
Improve and test jetty.sh behaviors (#10753)
* Issue #10696 - Addressing start-stop-daemon behaviors in jetty.sh * disable internal pid-file management of start-stop-daemon * IssueDo not test for file system permissions if user is root, or process will switch to JETTY_USER * Fixing bad UID / JETTY_USER condition * Avoid FS test with setuid use as well * Fixing stop behavior * Adding jetty.sh docker testing --------- Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com> Signed-off-by: Olivier Lamy <olamy@apache.org> Co-authored-by: Olivier Lamy <olamy@apache.org>
1 parent 8b5deea commit 92f62a1

File tree

15 files changed

+622
-32
lines changed

15 files changed

+622
-32
lines changed

jetty-home/src/main/resources/bin/jetty.sh

+97-32
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,46 @@ pidKill()
216216
fi
217217
}
218218

219+
testFileSystemPermissions()
220+
{
221+
# Don't test file system permissions if user is root
222+
if [ $UID -eq 0 ] ; then
223+
(( DEBUG )) && echo "Not testing file system permissions: uid is 0"
224+
return 0
225+
fi
226+
227+
# Don't test if JETTY_USER is specified
228+
# as the Jetty process will switch to a different user id on startup
229+
if [ -n "$JETTY_USER" ] ; then
230+
(( DEBUG )) && echo "Not testing file system permissions: JETTY_USER=$JETTY_USER"
231+
return 0
232+
fi
233+
234+
# Don't test if setuid is specified
235+
# as the Jetty process will switch to a different user id on startup
236+
if expr "${JETTY_ARGS[*]}" : '.*setuid.*' >/dev/null
237+
then
238+
(( DEBUG )) && echo "Not testing file system permissions: setuid in use"
239+
return 0
240+
fi
241+
242+
# Test if PID can be written from this userid
243+
if ! touch "$JETTY_PID"
244+
then
245+
echo "** ERROR: Unable to touch file: $JETTY_PID"
246+
echo " Correct issues preventing use of \$JETTY_PID and try again."
247+
exit 1
248+
fi
249+
250+
# Test if STATE can be written from this userid
251+
if ! touch "$JETTY_STATE"
252+
then
253+
echo "** ERROR: Unable to touch file: $JETTY_STATE"
254+
echo " Correct issues preventing use of \$JETTY_STATE and try again."
255+
exit 1
256+
fi
257+
}
258+
219259
readConfig()
220260
{
221261
(( DEBUG )) && echo "Reading $1.."
@@ -240,6 +280,10 @@ dumpEnv()
240280
echo "JETTY_START_TIMEOUT = $JETTY_START_TIMEOUT"
241281
echo "JETTY_SYS_PROPS = $JETTY_SYS_PROPS"
242282
echo "RUN_ARGS = ${RUN_ARGS[*]}"
283+
echo "ID = $(id)"
284+
echo "JETTY_USER = $JETTY_USER"
285+
echo "USE_START_STOP_DAEMON = $USE_START_STOP_DAEMON"
286+
echo "START_STOP_DAEMON = $START_STOP_DAEMON_AVAILABLE"
243287
}
244288

245289

@@ -249,6 +293,7 @@ dumpEnv()
249293
CONFIGS=()
250294
NO_START=0
251295
DEBUG=0
296+
USE_START_STOP_DAEMON=1
252297

253298
while [[ $1 = -* ]]; do
254299
case $1 in
@@ -404,14 +449,14 @@ case "`uname`" in
404449
CYGWIN*) JETTY_STATE="`cygpath -w $JETTY_STATE`";;
405450
esac
406451

407-
408452
JETTY_ARGS=(${JETTY_ARGS[*]} "jetty.state=$JETTY_STATE" "jetty.pid=$JETTY_PID")
409453

410454
##################################################
411455
# Get the list of config.xml files from jetty.conf
412456
##################################################
413457
if [ -f "$JETTY_CONF" ] && [ -r "$JETTY_CONF" ]
414458
then
459+
(( DEBUG )) && echo "$JETTY_CONF: (begin read) JETTY_ARGS.length=${#JETTY_ARGS[@]}"
415460
while read -r CONF
416461
do
417462
if expr "$CONF" : '#' >/dev/null ; then
@@ -427,16 +472,17 @@ then
427472
do
428473
if [ -r "$XMLFILE" ] && [ -f "$XMLFILE" ]
429474
then
430-
JETTY_ARGS=(${JETTY_ARGS[*]} "$XMLFILE")
475+
JETTY_ARGS[${#JETTY_ARGS[@]}]=$XMLFILE
431476
else
432477
echo "** WARNING: Cannot read '$XMLFILE' specified in '$JETTY_CONF'"
433478
fi
434479
done
435480
else
436481
# assume it's a command line parameter (let start.jar deal with its validity)
437-
JETTY_ARGS=(${JETTY_ARGS[*]} "$CONF")
482+
JETTY_ARGS[${#JETTY_ARGS[@]}]=$CONF
438483
fi
439484
done < "$JETTY_CONF"
485+
(( DEBUG )) && echo "$JETTY_CONF: (finished read) JETTY_ARGS.length=${#JETTY_ARGS[@]}"
440486
fi
441487

442488
##################################################
@@ -507,8 +553,22 @@ case "`uname`" in
507553
CYGWIN*) JETTY_START="`cygpath -w $JETTY_START`";;
508554
esac
509555

556+
# Determine if we can use start-stop-daemon or not
557+
START_STOP_DAEMON_AVAILABLE=0
558+
559+
if (( USE_START_STOP_DAEMON ))
560+
then
561+
# only if root user is executing jetty.sh, and the start-stop-daemon exists
562+
if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1
563+
then
564+
START_STOP_DAEMON_AVAILABLE=1
565+
else
566+
USE_START_STOP_DAEMON=0
567+
fi
568+
fi
569+
510570
# Collect the dry-run (of opts,path,main,args) from the jetty.base configuration
511-
JETTY_DRY_RUN=$("$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS[*]} ${JAVA_OPTIONS[*]})
571+
JETTY_DRY_RUN=$(echo "${JETTY_ARGS[*]} ${JAVA_OPTIONS[*]}" | xargs "$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args)
512572
RUN_ARGS=($JETTY_SYS_PROPS ${JETTY_DRY_RUN[@]})
513573

514574
if (( DEBUG ))
@@ -531,38 +591,27 @@ case "$ACTION" in
531591
exit
532592
fi
533593

534-
if ! touch "$JETTY_PID"
535-
then
536-
echo "** ERROR: Unable to touch file: $JETTY_PID"
537-
echo " Correct issues preventing use of \$JETTY_PID and try again."
538-
exit 1
539-
fi
540-
541-
if ! touch "$JETTY_STATE"
542-
then
543-
echo "** ERROR: Unable to touch file: $JETTY_STATE"
544-
echo " Correct issues preventing use of \$JETTY_STATE and try again."
545-
exit 1
546-
fi
594+
testFileSystemPermissions
547595

548596
echo -n "Starting Jetty: "
549597

550598
# Startup from a service file
551-
if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1
599+
if (( USE_START_STOP_DAEMON ))
552600
then
553601
unset CH_USER
554602
if [ -n "$JETTY_USER" ]
555603
then
556604
CH_USER="--chuid $JETTY_USER"
557605
fi
558606

559-
echo ${RUN_ARGS[@]} --start-log-file="$JETTY_START_LOG" | xargs start-stop-daemon \
607+
# use of --pidfile /dev/null disables internal pidfile
608+
# management of the start-stop-daemon (see man page)
609+
echo ${RUN_ARGS[@]} | xargs start-stop-daemon \
560610
--start $CH_USER \
561-
--pidfile "$JETTY_PID" \
611+
--pidfile /dev/null \
562612
--chdir "$JETTY_BASE" \
563613
--background \
564-
--output "${JETTY_RUN}/start-stop.log"
565-
--make-pidfile \
614+
--output "${JETTY_RUN}/start-stop.log" \
566615
--startas "$JAVA" \
567616
--
568617
(( DEBUG )) && echo "Starting: start-stop-daemon"
@@ -618,25 +667,41 @@ case "$ACTION" in
618667

619668
stop)
620669
echo -n "Stopping Jetty: "
621-
# Stop from a service file
622-
if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1; then
623-
start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s HUP
670+
if [ ! -r "$JETTY_PID" ] ; then
671+
echo "** ERROR: no pid found at $JETTY_PID"
672+
exit 1
673+
fi
674+
675+
PID=$(tail -1 "$JETTY_PID")
676+
if [ -z "$PID" ] ; then
677+
echo "** ERROR: no pid found in $JETTY_PID"
678+
exit 1
679+
fi
680+
681+
# Stopping service started with start-stop-daemon
682+
if (( USE_START_STOP_DAEMON )) ; then
683+
(( DEBUG )) && echo "Issuing HUP to $PID"
684+
start-stop-daemon --stop \
685+
--pid "$PID" \
686+
--chdir "$JETTY_BASE" \
687+
--startas "$JAVA" \
688+
--signal HUP
624689

625690
TIMEOUT=30
626691
while running "$JETTY_PID"; do
692+
(( DEBUG )) && echo "Issuing KILL to $PID"
627693
if (( TIMEOUT-- == 0 )); then
628-
start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s KILL
694+
start-stop-daemon --stop \
695+
--pid "$PID" \
696+
--chdir "$JETTY_BASE" \
697+
--startas "$JAVA" \
698+
--signal KILL
629699
fi
630700

631701
sleep 1
632702
done
633703
else
634-
# Stop from a non-service path
635-
if [ ! -r "$JETTY_PID" ] ; then
636-
echo "** ERROR: no pid found at $JETTY_PID"
637-
exit 1
638-
fi
639-
704+
# Stopping from non-service start
640705
pidKill "$JETTY_PID" 30
641706
fi
642707

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// ========================================================================
3+
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
4+
//
5+
// This program and the accompanying materials are made available under the
6+
// terms of the Eclipse Public License v. 2.0 which is available at
7+
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
8+
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
//
10+
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
11+
// ========================================================================
12+
//
13+
14+
package org.eclipse.jetty.tests.distribution.jettysh;
15+
16+
import java.util.function.Consumer;
17+
18+
import org.testcontainers.images.builder.ImageFromDockerfile;
19+
import org.testcontainers.images.builder.dockerfile.DockerfileBuilder;
20+
21+
/**
22+
* Simplify the use of {@link ImageFromDockerfile} to get some sanity in naming convention
23+
* and {@link #toString()} behaviors so that Test execution makes sense.
24+
*/
25+
public class ImageFromDSL extends ImageFromDockerfile
26+
{
27+
private ImageFromDSL parentImage;
28+
29+
public ImageFromDSL(ImageFromDSL baseImage, String suffix, Consumer<DockerfileBuilder> builderConsumer)
30+
{
31+
this(baseImage.getDockerImageName() + "-" + suffix, builderConsumer);
32+
this.parentImage = baseImage;
33+
}
34+
35+
public ImageFromDSL(String name, Consumer<DockerfileBuilder> builderConsumer)
36+
{
37+
super(name, false);
38+
withDockerfileFromBuilder(builderConsumer);
39+
}
40+
41+
public ImageFromDSL getParentImage()
42+
{
43+
return parentImage;
44+
}
45+
46+
@Override
47+
public String toString()
48+
{
49+
return getDockerImageName();
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//
2+
// ========================================================================
3+
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
4+
//
5+
// This program and the accompanying materials are made available under the
6+
// terms of the Eclipse Public License v. 2.0 which is available at
7+
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
8+
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
//
10+
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
11+
// ========================================================================
12+
//
13+
14+
package org.eclipse.jetty.tests.distribution.jettysh;
15+
16+
import java.io.File;
17+
import java.nio.file.Path;
18+
import java.util.function.Consumer;
19+
20+
import org.eclipse.jetty.tests.hometester.JettyHomeTester;
21+
import org.testcontainers.images.builder.dockerfile.DockerfileBuilder;
22+
23+
public abstract class ImageOS extends ImageFromDSL
24+
{
25+
public static final String REGISTRY = "registry.jetty.org";
26+
public static final String REPOSITORY = REGISTRY + "/jetty-sh";
27+
28+
private Path jettyHomePath;
29+
30+
public ImageOS(String osid, Consumer<DockerfileBuilder> builderConsumer)
31+
{
32+
super(REPOSITORY + ":" + osid, builderConsumer);
33+
}
34+
35+
protected File getJettyHomeDir()
36+
{
37+
if (jettyHomePath == null)
38+
{
39+
String jettyVersion = System.getProperty("jettyVersion");
40+
try
41+
{
42+
JettyHomeTester homeTester = JettyHomeTester.Builder.newInstance()
43+
.jettyVersion(jettyVersion)
44+
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
45+
.build();
46+
jettyHomePath = homeTester.getJettyHome();
47+
}
48+
catch (Exception e)
49+
{
50+
throw new RuntimeException("Unable to get unpacked JETTY_HOME dir", e);
51+
}
52+
}
53+
return jettyHomePath.toFile();
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//
2+
// ========================================================================
3+
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
4+
//
5+
// This program and the accompanying materials are made available under the
6+
// terms of the Eclipse Public License v. 2.0 which is available at
7+
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
8+
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
//
10+
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
11+
// ========================================================================
12+
//
13+
14+
package org.eclipse.jetty.tests.distribution.jettysh;
15+
16+
/**
17+
* An OS Image of linux specific for running on Amazon AWS.
18+
* This is based on Amazon Linux 2 (which is based on Alpine 3).
19+
* Amazon Corretto JDK 11 is installed.
20+
* This image does NOT come with start-stop-daemon installed.
21+
* Instead of apt, it uses yum (the redhat package manager)
22+
*/
23+
public class ImageOSAmazonCorretto11 extends ImageOS
24+
{
25+
public ImageOSAmazonCorretto11()
26+
{
27+
super("amazoncorretto-jdk11",
28+
builder ->
29+
builder
30+
.from("amazoncorretto:11.0.20")
31+
.run("yum update -y ; " +
32+
"yum install -y curl tar gzip vim shadow-utils net-tools")
33+
.env("TEST_DIR", "/var/test")
34+
.env("JETTY_HOME", "$TEST_DIR/jetty-home")
35+
.env("JETTY_BASE", "$TEST_DIR/jetty-base")
36+
.env("PATH", "$PATH:${JETTY_HOME}/bin/")
37+
.user("root")
38+
// Configure /etc/default/jetty
39+
.run("echo \"JETTY_HOME=${JETTY_HOME}\" > /etc/default/jetty ; " +
40+
"echo \"JETTY_BASE=${JETTY_BASE}\" >> /etc/default/jetty ; " +
41+
"echo \"JETTY_RUN=${JETTY_BASE}\" >> /etc/default/jetty ")
42+
// setup Jetty Home
43+
.copy("/opt/jetty/", "${JETTY_HOME}/")
44+
.env("PATH", "$PATH:${JETTY_HOME}/bin/")
45+
.run("chmod ugo+x ${JETTY_HOME}/bin/jetty.sh")
46+
.build()
47+
);
48+
withFileFromFile("/opt/jetty/", getJettyHomeDir());
49+
}
50+
}

0 commit comments

Comments
 (0)