mirror of
https://github.com/skeeto/endlessh.git
synced 2025-12-10 06:55:36 +00:00
Compare commits
1 Commits
1.0
...
special-me
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30e3ff1f32 |
9
Makefile
9
Makefile
@@ -3,18 +3,9 @@ CC = cc
|
|||||||
CFLAGS = -std=c99 -Wall -Wextra -Wno-missing-field-initializers -Os
|
CFLAGS = -std=c99 -Wall -Wextra -Wno-missing-field-initializers -Os
|
||||||
LDFLAGS = -ggdb3
|
LDFLAGS = -ggdb3
|
||||||
LDLIBS =
|
LDLIBS =
|
||||||
PREFIX = /usr/local
|
|
||||||
|
|
||||||
all: endlessh
|
|
||||||
|
|
||||||
endlessh: endlessh.c
|
endlessh: endlessh.c
|
||||||
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ endlessh.c $(LDLIBS)
|
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ endlessh.c $(LDLIBS)
|
||||||
|
|
||||||
install: endlessh
|
|
||||||
install -d $(DESTDIR)$(PREFIX)/bin
|
|
||||||
install -m 755 endlessh $(DESTDIR)$(PREFIX)/bin/
|
|
||||||
install -d $(DESTDIR)$(PREFIX)/share/man/man1
|
|
||||||
install -m 644 endlessh.1 $(DESTDIR)$(PREFIX)/share/man/man1/
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf endlessh
|
rm -rf endlessh
|
||||||
|
|||||||
35
README.md
35
README.md
@@ -19,8 +19,6 @@ Usage information is printed with `-h`.
|
|||||||
|
|
||||||
```
|
```
|
||||||
Usage: endlessh [-vh] [-d MS] [-f CONFIG] [-l LEN] [-m LIMIT] [-p PORT]
|
Usage: endlessh [-vh] [-d MS] [-f CONFIG] [-l LEN] [-m LIMIT] [-p PORT]
|
||||||
-4 Bind to IPv4 only
|
|
||||||
-6 Bind to IPv6 only
|
|
||||||
-d INT Message millisecond delay [10000]
|
-d INT Message millisecond delay [10000]
|
||||||
-f Set and load config file [/etc/endlessh/config]
|
-f Set and load config file [/etc/endlessh/config]
|
||||||
-h Print this help message and exit
|
-h Print this help message and exit
|
||||||
@@ -45,8 +43,6 @@ write a complete, consistent log.
|
|||||||
|
|
||||||
A SIGHUP signal requests a reload of the configuration file (`-f`).
|
A SIGHUP signal requests a reload of the configuration file (`-f`).
|
||||||
|
|
||||||
A SIGUSR1 signal will print connections stats to the log.
|
|
||||||
|
|
||||||
## Sample Configuration File
|
## Sample Configuration File
|
||||||
|
|
||||||
The configuration file has similar syntax to OpenSSH.
|
The configuration file has similar syntax to OpenSSH.
|
||||||
@@ -73,40 +69,15 @@ MaxClients 4096
|
|||||||
# 1 = Standard, useful log messages
|
# 1 = Standard, useful log messages
|
||||||
# 2 = Very noisy debugging information
|
# 2 = Very noisy debugging information
|
||||||
LogLevel 0
|
LogLevel 0
|
||||||
|
|
||||||
# Set the family of the listening socket
|
|
||||||
# 0 = Use IPv4 Mapped IPv6 (Both v4 and v6, default)
|
|
||||||
# 4 = Use IPv4 only
|
|
||||||
# 6 = Use IPv6 only
|
|
||||||
BindFamily 0
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build issues
|
## Build issues
|
||||||
|
|
||||||
Some more esoteric systems require extra configuration when building.
|
RHEL 6 and CentOS 6 use a version of glibc older than 2.17 (December
|
||||||
|
2012), and `clock_gettime(2)` is still in librt. For these systems you
|
||||||
### RHEL 6 / CentOS 6
|
will need to link against librt:
|
||||||
|
|
||||||
This system uses a version of glibc older than 2.17 (December 2012), and
|
|
||||||
`clock_gettime(2)` is still in librt. For these systems you will need to
|
|
||||||
link against librt:
|
|
||||||
|
|
||||||
make LDLIBS=-lrt
|
make LDLIBS=-lrt
|
||||||
|
|
||||||
### Solaris / illumos
|
|
||||||
|
|
||||||
These systems don't include all the necessary functionality in libc and
|
|
||||||
the linker requires some extra libraries:
|
|
||||||
|
|
||||||
make CC=gcc LDLIBS='-lnsl -lrt -lsocket'
|
|
||||||
|
|
||||||
If you're not using GCC or Clang, also override `CFLAGS` and `LDFLAGS`
|
|
||||||
to remove GCC-specific options. For example, on Solaris:
|
|
||||||
|
|
||||||
make CFLAGS=-fast LDFLAGS= LDLIBS='-lnsl -lrt -lsocket'
|
|
||||||
|
|
||||||
The feature test macros on these systems isn't reliable, so you may also
|
|
||||||
need to use `-D__EXTENSIONS__` in `CFLAGS`.
|
|
||||||
|
|
||||||
|
|
||||||
[np]: https://nullprogram.com/blog/2019/03/22/
|
[np]: https://nullprogram.com/blog/2019/03/22/
|
||||||
|
|||||||
81
endlessh.1
81
endlessh.1
@@ -1,81 +0,0 @@
|
|||||||
.Dd $Mdocdate: April 12 2019 $
|
|
||||||
.Dt ENDLESSH 1
|
|
||||||
.Os
|
|
||||||
.Sh NAME
|
|
||||||
.Nm endless
|
|
||||||
.Nd An SSH tarpit
|
|
||||||
.Sh SYNOPSIS
|
|
||||||
.Nm endless
|
|
||||||
.Op Fl 46chvV
|
|
||||||
.Op Fl d Ar delay
|
|
||||||
.Op Fl f Ar config
|
|
||||||
.Op Fl l Ar max banner length
|
|
||||||
.Op Fl m Ar max clients
|
|
||||||
.Op Fl p Ar port
|
|
||||||
.Sh DESCRIPTION
|
|
||||||
.Nm
|
|
||||||
is an SSH tarpit that very slowly
|
|
||||||
sends an endless, random SSH banner.
|
|
||||||
.Pp
|
|
||||||
.Nm
|
|
||||||
keeps SSH clients locked up for hours or even days at a time.
|
|
||||||
The purpose is to put your real SSH server on another port
|
|
||||||
and then let the script kiddies get stuck in this tarpit
|
|
||||||
instead of bothering a real server.
|
|
||||||
.Pp
|
|
||||||
Since the tarpit is in the banner before any cryptographic
|
|
||||||
exchange occurs, this program doesn't depend on any cryptographic
|
|
||||||
libraries. It's a simple, single-threaded, standalone C program.
|
|
||||||
It uses poll() to trap multiple clients at a time.
|
|
||||||
.Pp
|
|
||||||
The options are as follows:
|
|
||||||
.Bl -tag -width Ds
|
|
||||||
.It Fl 4
|
|
||||||
Forces
|
|
||||||
.Nm
|
|
||||||
to use IPv4 addresses only.
|
|
||||||
.It Fl 6
|
|
||||||
Forces
|
|
||||||
.Nm
|
|
||||||
to use IPv6 addresses only.
|
|
||||||
.It Fl d Ar delay
|
|
||||||
Message milliseconds delay. Default: 10000
|
|
||||||
.It Fl f Ar config
|
|
||||||
Set and load config file.
|
|
||||||
By default
|
|
||||||
.Nm
|
|
||||||
looks for /etc/endlessh/config.
|
|
||||||
.It Fl h
|
|
||||||
Print the help message and exit.
|
|
||||||
.It Fl l Ar max banner length
|
|
||||||
Maximum banner line length (3-255). Default: 32
|
|
||||||
.It Fl m Ar max clients
|
|
||||||
Maximum number of clients. Default: 4096
|
|
||||||
.It Fl p Ar port
|
|
||||||
Set the listening port. By default
|
|
||||||
.Nm
|
|
||||||
listens on port 2222.
|
|
||||||
.It Fl v
|
|
||||||
Print diagnostics to standard output. Can be specified
|
|
||||||
numerous times to increase verbosity.
|
|
||||||
.It Fl V
|
|
||||||
Causes
|
|
||||||
.Nm
|
|
||||||
to print version information and exit.
|
|
||||||
.El
|
|
||||||
.El
|
|
||||||
.Pp
|
|
||||||
If
|
|
||||||
.Nm
|
|
||||||
receives the SIGTERM signal it will gracefully shut
|
|
||||||
down the daemon, allowing it to write a complete, consistent log.
|
|
||||||
.Pp
|
|
||||||
A SIGHUP signal requests a reload of its configuration file.
|
|
||||||
.Pp
|
|
||||||
A SIGUSR1 signal will print connections stats to the log.
|
|
||||||
.Sh FILES
|
|
||||||
.Bl -tag -width /etc/endlessh/config -compact
|
|
||||||
.It Pa /etc/endlessh/config
|
|
||||||
The default
|
|
||||||
.Nm
|
|
||||||
configuration file.
|
|
||||||
392
endlessh.c
392
endlessh.c
@@ -1,4 +1,22 @@
|
|||||||
#define _XOPEN_SOURCE 600
|
#ifdef __FreeBSD__
|
||||||
|
# define _WITH_GETLINE
|
||||||
|
/* The MSG_DONTWAIT send(2) flag is non-standard, but widely available.
|
||||||
|
* However, FreeBSD doesn't define this flag when using POSIX feature
|
||||||
|
* test macros. Normally feature test macros are required to expose
|
||||||
|
* POSIX functionality, though FreeBSD isn't strict about this. In a
|
||||||
|
* sense it's technically correct to hide a non-standard flag when
|
||||||
|
* asking for strict standards compliance, but this behavior makes this
|
||||||
|
* flag impossible to use in portable programs, at least without this
|
||||||
|
* sort of special case.
|
||||||
|
*
|
||||||
|
* To get the prototype for getline(3), we need either a POSIX feature
|
||||||
|
* test macro or use the FreeBSD-specific _WITH_GETLINE macro. Since we
|
||||||
|
* can't use the former, we'll have to go with the latter.
|
||||||
|
*/
|
||||||
|
#else
|
||||||
|
# define _POSIX_C_SOURCE 200809L
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -9,27 +27,28 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
#define ENDLESSH_VERSION 1.0
|
#define ENDLESSH_VERSION 0.1
|
||||||
|
|
||||||
#define DEFAULT_PORT 2222
|
#define DEFAULT_PORT 2222
|
||||||
#define DEFAULT_DELAY 10000 /* milliseconds */
|
#define DEFAULT_DELAY 10000 /* milliseconds */
|
||||||
#define DEFAULT_MAX_LINE_LENGTH 32
|
#define DEFAULT_MAX_LINE_LENGTH 32
|
||||||
#define DEFAULT_MAX_CLIENTS 4096
|
#define DEFAULT_MAX_CLIENTS 4096
|
||||||
#define DEFAULT_CONFIG_FILE "/etc/endlessh/config"
|
#define DEFAULT_CONFIG_FILE "/etc/endlessh/config"
|
||||||
#define DEFAULT_BIND_FAMILY AF_UNSPEC
|
|
||||||
|
#define FLAG_SPECIAL_SENT (1 << 0)
|
||||||
|
#define SPECIAL_MESSAGE "Stay awhile and listen\r\n"
|
||||||
|
|
||||||
#define XSTR(s) STR(s)
|
#define XSTR(s) STR(s)
|
||||||
#define STR(s) #s
|
#define STR(s) #s
|
||||||
|
|
||||||
static long long
|
static long long
|
||||||
epochms(void)
|
uepoch(void)
|
||||||
{
|
{
|
||||||
struct timespec tv;
|
struct timespec tv;
|
||||||
clock_gettime(CLOCK_REALTIME, &tv);
|
clock_gettime(CLOCK_REALTIME, &tv);
|
||||||
@@ -49,7 +68,7 @@ logmsg(enum loglevel level, const char *format, ...)
|
|||||||
int save = errno;
|
int save = errno;
|
||||||
|
|
||||||
/* Print a timestamp */
|
/* Print a timestamp */
|
||||||
long long now = epochms();
|
long long now = uepoch();
|
||||||
time_t t = now / 1000;
|
time_t t = now / 1000;
|
||||||
char date[64];
|
char date[64];
|
||||||
struct tm tm[1];
|
struct tm tm[1];
|
||||||
@@ -67,12 +86,6 @@ logmsg(enum loglevel level, const char *format, ...)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct {
|
|
||||||
long long connects;
|
|
||||||
long long milliseconds;
|
|
||||||
long long bytes_sent;
|
|
||||||
} statistics;
|
|
||||||
|
|
||||||
struct client {
|
struct client {
|
||||||
char ipaddr[INET6_ADDRSTRLEN];
|
char ipaddr[INET6_ADDRSTRLEN];
|
||||||
long long connect_time;
|
long long connect_time;
|
||||||
@@ -81,6 +94,7 @@ struct client {
|
|||||||
struct client *next;
|
struct client *next;
|
||||||
int port;
|
int port;
|
||||||
int fd;
|
int fd;
|
||||||
|
int flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct client *
|
static struct client *
|
||||||
@@ -89,12 +103,12 @@ client_new(int fd, long long send_next)
|
|||||||
struct client *c = malloc(sizeof(*c));
|
struct client *c = malloc(sizeof(*c));
|
||||||
if (c) {
|
if (c) {
|
||||||
c->ipaddr[0] = 0;
|
c->ipaddr[0] = 0;
|
||||||
c->connect_time = epochms();
|
c->connect_time = uepoch();
|
||||||
c->send_next = send_next;
|
c->send_next = send_next;
|
||||||
c->bytes_sent = 0;
|
c->bytes_sent = 0;
|
||||||
c->next = 0;
|
c->next = 0;
|
||||||
c->fd = fd;
|
c->fd = fd;
|
||||||
c->port = 0;
|
c->flags = 0;
|
||||||
|
|
||||||
/* Set the smallest possible recieve buffer. This reduces local
|
/* Set the smallest possible recieve buffer. This reduces local
|
||||||
* resource usage and slows down the remote end.
|
* resource usage and slows down the remote end.
|
||||||
@@ -129,57 +143,59 @@ static void
|
|||||||
client_destroy(struct client *client)
|
client_destroy(struct client *client)
|
||||||
{
|
{
|
||||||
logmsg(LOG_DEBUG, "close(%d)", client->fd);
|
logmsg(LOG_DEBUG, "close(%d)", client->fd);
|
||||||
long long dt = epochms() - client->connect_time;
|
long long dt = uepoch() - client->connect_time;
|
||||||
logmsg(LOG_INFO,
|
logmsg(LOG_INFO,
|
||||||
"CLOSE host=%s port=%d fd=%d "
|
"CLOSE host=%s port=%d fd=%d "
|
||||||
"time=%lld.%03lld bytes=%lld",
|
"time=%lld.%03lld bytes=%lld",
|
||||||
client->ipaddr, client->port, client->fd,
|
client->ipaddr, client->port, client->fd,
|
||||||
dt / 1000, dt % 1000,
|
dt / 1000, dt % 1000,
|
||||||
client->bytes_sent);
|
client->bytes_sent);
|
||||||
statistics.milliseconds += dt;
|
|
||||||
close(client->fd);
|
close(client->fd);
|
||||||
free(client);
|
free(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
struct queue {
|
||||||
statistics_log_totals(struct client *clients)
|
|
||||||
{
|
|
||||||
long long milliseconds = statistics.milliseconds;
|
|
||||||
for (long long now = epochms(); clients; clients = clients->next)
|
|
||||||
milliseconds += now - clients->connect_time;
|
|
||||||
logmsg(LOG_INFO, "TOTALS connects=%lld seconds=%lld.%03lld bytes=%lld",
|
|
||||||
statistics.connects,
|
|
||||||
milliseconds / 1000,
|
|
||||||
milliseconds % 1000,
|
|
||||||
statistics.bytes_sent);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct fifo {
|
|
||||||
struct client *head;
|
struct client *head;
|
||||||
struct client *tail;
|
struct client *tail;
|
||||||
int length;
|
int length;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fifo_init(struct fifo *q)
|
queue_init(struct queue *q)
|
||||||
{
|
{
|
||||||
q->head = q->tail = 0;
|
q->head = q->tail = 0;
|
||||||
q->length = 0;
|
q->length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct client *
|
static struct client *
|
||||||
fifo_pop(struct fifo *q)
|
queue_remove(struct queue *q, int fd)
|
||||||
{
|
{
|
||||||
struct client *removed = q->head;
|
/* Yes, this is a linear search, but the element we're looking for
|
||||||
q->head = q->head->next;
|
* is virtually always one of the first few elements.
|
||||||
removed->next = 0;
|
*/
|
||||||
if (!--q->length)
|
struct client *c;
|
||||||
q->tail = 0;
|
struct client *prev = 0;
|
||||||
return removed;
|
for (c = q->head; c; prev = c, c = c->next) {
|
||||||
|
if (c->fd == fd) {
|
||||||
|
if (!--q->length) {
|
||||||
|
q->head = q->tail = 0;
|
||||||
|
} else if (q->tail == c) {
|
||||||
|
q->tail = prev;
|
||||||
|
prev->next = 0;
|
||||||
|
} else if (prev) {
|
||||||
|
prev->next = c->next;
|
||||||
|
} else {
|
||||||
|
q->head = c->next;
|
||||||
|
}
|
||||||
|
c->next = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fifo_append(struct fifo *q, struct client *c)
|
queue_append(struct queue *q, struct client *c)
|
||||||
{
|
{
|
||||||
if (!q->tail) {
|
if (!q->tail) {
|
||||||
q->head = q->tail = c;
|
q->head = q->tail = c;
|
||||||
@@ -191,7 +207,7 @@ fifo_append(struct fifo *q, struct client *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fifo_destroy(struct fifo *q)
|
queue_destroy(struct queue *q)
|
||||||
{
|
{
|
||||||
struct client *c = q->head;
|
struct client *c = q->head;
|
||||||
while (c) {
|
while (c) {
|
||||||
@@ -203,6 +219,56 @@ fifo_destroy(struct fifo *q)
|
|||||||
q->length = 0;
|
q->length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct pollvec {
|
||||||
|
size_t cap;
|
||||||
|
size_t fill;
|
||||||
|
struct pollfd *fds;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
pollvec_init(struct pollvec *v)
|
||||||
|
{
|
||||||
|
v->cap = 4;
|
||||||
|
v->fill = 0;
|
||||||
|
v->fds = malloc(v->cap * sizeof(v->fds[0]));
|
||||||
|
if (!v->fds) {
|
||||||
|
fprintf(stderr, "endlessh: pollvec initialization failed\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pollvec_clear(struct pollvec *v)
|
||||||
|
{
|
||||||
|
v->fill = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
pollvec_push(struct pollvec *v, int fd, short events)
|
||||||
|
{
|
||||||
|
if (v->cap == v->fill) {
|
||||||
|
size_t size = v->cap * 2 * sizeof(v->fds[0]);
|
||||||
|
if (size < v->cap * sizeof(v->fds[0]))
|
||||||
|
return 0; // overflow
|
||||||
|
struct pollfd *grow = realloc(v->fds, size);
|
||||||
|
if (!grow)
|
||||||
|
return 0;
|
||||||
|
v->fds = grow;
|
||||||
|
v->cap *= 2;
|
||||||
|
}
|
||||||
|
v->fds[v->fill].fd = fd;
|
||||||
|
v->fds[v->fill].events = events;
|
||||||
|
v->fill++;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pollvec_free(struct pollvec *v)
|
||||||
|
{
|
||||||
|
free(v->fds);
|
||||||
|
v->fds = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
die(void)
|
die(void)
|
||||||
{
|
{
|
||||||
@@ -248,21 +314,11 @@ sighup_handler(int signal)
|
|||||||
reload = 1;
|
reload = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static volatile sig_atomic_t dumpstats = 0;
|
|
||||||
|
|
||||||
static void
|
|
||||||
sigusr1_handler(int signal)
|
|
||||||
{
|
|
||||||
(void)signal;
|
|
||||||
dumpstats = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct config {
|
struct config {
|
||||||
int port;
|
int port;
|
||||||
int delay;
|
int delay;
|
||||||
int max_line_length;
|
int max_line_length;
|
||||||
int max_clients;
|
int max_clients;
|
||||||
int bind_family;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CONFIG_DEFAULT { \
|
#define CONFIG_DEFAULT { \
|
||||||
@@ -270,7 +326,6 @@ struct config {
|
|||||||
.delay = DEFAULT_DELAY, \
|
.delay = DEFAULT_DELAY, \
|
||||||
.max_line_length = DEFAULT_MAX_LINE_LENGTH, \
|
.max_line_length = DEFAULT_MAX_LINE_LENGTH, \
|
||||||
.max_clients = DEFAULT_MAX_CLIENTS, \
|
.max_clients = DEFAULT_MAX_CLIENTS, \
|
||||||
.bind_family = DEFAULT_BIND_FAMILY, \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -333,27 +388,6 @@ config_set_max_line_length(struct config *c, const char *s, int hardfail)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
config_set_bind_family(struct config *c, const char *s, int hardfail)
|
|
||||||
{
|
|
||||||
switch (*s) {
|
|
||||||
case '4':
|
|
||||||
c->bind_family = AF_INET;
|
|
||||||
break;
|
|
||||||
case '6':
|
|
||||||
c->bind_family = AF_INET6;
|
|
||||||
break;
|
|
||||||
case '0':
|
|
||||||
c->bind_family = AF_UNSPEC;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "endlessh: Invalid address family: %s\n", s);
|
|
||||||
if (hardfail)
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum config_key {
|
enum config_key {
|
||||||
KEY_INVALID,
|
KEY_INVALID,
|
||||||
KEY_PORT,
|
KEY_PORT,
|
||||||
@@ -361,7 +395,6 @@ enum config_key {
|
|||||||
KEY_MAX_LINE_LENGTH,
|
KEY_MAX_LINE_LENGTH,
|
||||||
KEY_MAX_CLIENTS,
|
KEY_MAX_CLIENTS,
|
||||||
KEY_LOG_LEVEL,
|
KEY_LOG_LEVEL,
|
||||||
KEY_BIND_FAMILY,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum config_key
|
static enum config_key
|
||||||
@@ -373,7 +406,6 @@ config_key_parse(const char *tok)
|
|||||||
[KEY_MAX_LINE_LENGTH] = "MaxLineLength",
|
[KEY_MAX_LINE_LENGTH] = "MaxLineLength",
|
||||||
[KEY_MAX_CLIENTS] = "MaxClients",
|
[KEY_MAX_CLIENTS] = "MaxClients",
|
||||||
[KEY_LOG_LEVEL] = "LogLevel",
|
[KEY_LOG_LEVEL] = "LogLevel",
|
||||||
[KEY_BIND_FAMILY] = "BindFamily"
|
|
||||||
};
|
};
|
||||||
for (size_t i = 1; i < sizeof(table) / sizeof(*table); i++)
|
for (size_t i = 1; i < sizeof(table) / sizeof(*table); i++)
|
||||||
if (!strcmp(tok, table[i]))
|
if (!strcmp(tok, table[i]))
|
||||||
@@ -387,8 +419,9 @@ config_load(struct config *c, const char *file, int hardfail)
|
|||||||
long lineno = 0;
|
long lineno = 0;
|
||||||
FILE *f = fopen(file, "r");
|
FILE *f = fopen(file, "r");
|
||||||
if (f) {
|
if (f) {
|
||||||
char line[256];
|
size_t len = 0;
|
||||||
while (fgets(line, sizeof(line), f)) {
|
char *line = 0;
|
||||||
|
while (getline(&line, &len, f) != -1) {
|
||||||
lineno++;
|
lineno++;
|
||||||
|
|
||||||
/* Remove comments */
|
/* Remove comments */
|
||||||
@@ -440,9 +473,6 @@ config_load(struct config *c, const char *file, int hardfail)
|
|||||||
case KEY_MAX_CLIENTS:
|
case KEY_MAX_CLIENTS:
|
||||||
config_set_max_clients(c, tokens[1], hardfail);
|
config_set_max_clients(c, tokens[1], hardfail);
|
||||||
break;
|
break;
|
||||||
case KEY_BIND_FAMILY:
|
|
||||||
config_set_bind_family(c, tokens[1], hardfail);
|
|
||||||
break;
|
|
||||||
case KEY_LOG_LEVEL: {
|
case KEY_LOG_LEVEL: {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
char *end;
|
char *end;
|
||||||
@@ -458,6 +488,7 @@ config_load(struct config *c, const char *file, int hardfail)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(line);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -469,19 +500,13 @@ config_log(const struct config *c)
|
|||||||
logmsg(LOG_INFO, "Delay %ld", c->delay);
|
logmsg(LOG_INFO, "Delay %ld", c->delay);
|
||||||
logmsg(LOG_INFO, "MaxLineLength %d", c->max_line_length);
|
logmsg(LOG_INFO, "MaxLineLength %d", c->max_line_length);
|
||||||
logmsg(LOG_INFO, "MaxClients %d", c->max_clients);
|
logmsg(LOG_INFO, "MaxClients %d", c->max_clients);
|
||||||
logmsg(LOG_INFO, "BindFamily %s",
|
|
||||||
c->bind_family == AF_INET6 ? "IPv6 Only" :
|
|
||||||
c->bind_family == AF_INET ? "IPv4 Only" :
|
|
||||||
"IPv4 Mapped IPv6");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
usage(FILE *f)
|
usage(FILE *f)
|
||||||
{
|
{
|
||||||
fprintf(f, "Usage: endlessh [-vh] [-46] [-d MS] [-f CONFIG] [-l LEN] "
|
fprintf(f, "Usage: endlessh [-vh] [-d MS] [-f CONFIG] [-l LEN] "
|
||||||
"[-m LIMIT] [-p PORT]\n");
|
"[-m LIMIT] [-p PORT]\n");
|
||||||
fprintf(f, " -4 Bind to IPv4 only\n");
|
|
||||||
fprintf(f, " -6 Bind to IPv6 only\n");
|
|
||||||
fprintf(f, " -d INT Message millisecond delay ["
|
fprintf(f, " -d INT Message millisecond delay ["
|
||||||
XSTR(DEFAULT_DELAY) "]\n");
|
XSTR(DEFAULT_DELAY) "]\n");
|
||||||
fprintf(f, " -f Set and load config file ["
|
fprintf(f, " -f Set and load config file ["
|
||||||
@@ -504,11 +529,11 @@ print_version(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
server_create(int port, int family)
|
server_create(int port)
|
||||||
{
|
{
|
||||||
int r, s, value;
|
int r, s, value;
|
||||||
|
|
||||||
s = socket(family == AF_UNSPEC ? AF_INET6 : family, SOCK_STREAM, 0);
|
s = socket(AF_INET6, SOCK_STREAM, 0);
|
||||||
logmsg(LOG_DEBUG, "socket() = %d", s);
|
logmsg(LOG_DEBUG, "socket() = %d", s);
|
||||||
if (s == -1) die();
|
if (s == -1) die();
|
||||||
|
|
||||||
@@ -519,37 +544,12 @@ server_create(int port, int family)
|
|||||||
if (r == -1)
|
if (r == -1)
|
||||||
logmsg(LOG_DEBUG, "errno = %d, %s", errno, strerror(errno));
|
logmsg(LOG_DEBUG, "errno = %d, %s", errno, strerror(errno));
|
||||||
|
|
||||||
/*
|
struct sockaddr_in6 addr = {
|
||||||
* With OpenBSD IPv6 sockets are always IPv6-only, so the socket option
|
.sin6_family = AF_INET6,
|
||||||
* is read-only (not modifiable).
|
.sin6_port = htons(port),
|
||||||
* http://man.openbsd.org/ip6#IPV6_V6ONLY
|
.sin6_addr = in6addr_any
|
||||||
*/
|
};
|
||||||
#ifndef __OpenBSD__
|
r = bind(s, (void *)&addr, sizeof(addr));
|
||||||
if (family == AF_INET6 || family == AF_UNSPEC) {
|
|
||||||
errno = 0;
|
|
||||||
value = (family == AF_INET6);
|
|
||||||
r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
|
|
||||||
logmsg(LOG_DEBUG, "setsockopt(%d, IPV6_V6ONLY, true) = %d", s, r);
|
|
||||||
if (r == -1)
|
|
||||||
logmsg(LOG_DEBUG, "errno = %d, %s", errno, strerror(errno));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (family == AF_INET) {
|
|
||||||
struct sockaddr_in addr4 = {
|
|
||||||
.sin_family = AF_INET,
|
|
||||||
.sin_port = htons(port),
|
|
||||||
.sin_addr = {INADDR_ANY}
|
|
||||||
};
|
|
||||||
r = bind(s, (void *)&addr4, sizeof(addr4));
|
|
||||||
} else {
|
|
||||||
struct sockaddr_in6 addr6 = {
|
|
||||||
.sin6_family = AF_INET6,
|
|
||||||
.sin6_port = htons(port),
|
|
||||||
.sin6_addr = in6addr_any
|
|
||||||
};
|
|
||||||
r = bind(s, (void *)&addr6, sizeof(addr6));
|
|
||||||
}
|
|
||||||
logmsg(LOG_DEBUG, "bind(%d, port=%d) = %d", s, port, r);
|
logmsg(LOG_DEBUG, "bind(%d, port=%d) = %d", s, port, r);
|
||||||
if (r == -1) die();
|
if (r == -1) die();
|
||||||
|
|
||||||
@@ -560,33 +560,6 @@ server_create(int port, int family)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a line to a client, returning client if it's still up. */
|
|
||||||
static struct client *
|
|
||||||
sendline(struct client *client, int max_line_length, unsigned long *rng)
|
|
||||||
{
|
|
||||||
char line[256];
|
|
||||||
int len = randline(line, max_line_length, rng);
|
|
||||||
for (;;) {
|
|
||||||
ssize_t out = write(client->fd, line, len);
|
|
||||||
logmsg(LOG_DEBUG, "write(%d) = %d", client->fd, (int)out);
|
|
||||||
if (out == -1) {
|
|
||||||
if (errno == EINTR) {
|
|
||||||
continue; /* try again */
|
|
||||||
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
||||||
return client; /* don't care */
|
|
||||||
} else {
|
|
||||||
client_destroy(client);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
client->bytes_sent += out;
|
|
||||||
statistics.bytes_sent += out;
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -595,14 +568,8 @@ main(int argc, char **argv)
|
|||||||
config_load(&config, config_file, 1);
|
config_load(&config, config_file, 1);
|
||||||
|
|
||||||
int option;
|
int option;
|
||||||
while ((option = getopt(argc, argv, "46d:f:hl:m:p:vV")) != -1) {
|
while ((option = getopt(argc, argv, "d:f:hl:m:p:vV")) != -1) {
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case '4':
|
|
||||||
config_set_bind_family(&config, "4", 1);
|
|
||||||
break;
|
|
||||||
case '6':
|
|
||||||
config_set_bind_family(&config, "6", 1);
|
|
||||||
break;
|
|
||||||
case 'd':
|
case 'd':
|
||||||
config_set_delay(&config, optarg, 1);
|
config_set_delay(&config, optarg, 1);
|
||||||
break;
|
break;
|
||||||
@@ -624,7 +591,8 @@ main(int argc, char **argv)
|
|||||||
config_set_port(&config, optarg, 1);
|
config_set_port(&config, optarg, 1);
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
loglevel++;
|
if (!loglevel++)
|
||||||
|
setvbuf(stdout, 0, _IOLBF, 0);
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
print_version();
|
print_version();
|
||||||
@@ -641,9 +609,6 @@ main(int argc, char **argv)
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set output (log) to line buffered */
|
|
||||||
setvbuf(stdout, 0, _IOLBF, 0);
|
|
||||||
|
|
||||||
/* Log configuration */
|
/* Log configuration */
|
||||||
config_log(&config);
|
config_log(&config);
|
||||||
|
|
||||||
@@ -661,60 +626,53 @@ main(int argc, char **argv)
|
|||||||
if (r == -1)
|
if (r == -1)
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
{
|
|
||||||
struct sigaction sa = {.sa_handler = sigusr1_handler};
|
|
||||||
int r = sigaction(SIGUSR1, &sa, 0);
|
|
||||||
if (r == -1)
|
|
||||||
die();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct fifo fifo[1];
|
struct queue queue[1];
|
||||||
fifo_init(fifo);
|
queue_init(queue);
|
||||||
|
|
||||||
unsigned long rng = epochms();
|
struct pollvec pollvec[1];
|
||||||
|
pollvec_init(pollvec);
|
||||||
|
|
||||||
int server = server_create(config.port, config.bind_family);
|
unsigned long rng = uepoch();
|
||||||
|
|
||||||
|
int server = server_create(config.port);
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
if (reload) {
|
if (reload) {
|
||||||
/* Configuration reload requested (SIGHUP) */
|
/* Configuration reload requested (SIGHUP) */
|
||||||
int oldport = config.port;
|
int oldport = config.port;
|
||||||
int oldfamily = config.bind_family;
|
|
||||||
config_load(&config, config_file, 0);
|
config_load(&config, config_file, 0);
|
||||||
config_log(&config);
|
config_log(&config);
|
||||||
if (oldport != config.port || oldfamily != config.bind_family) {
|
if (oldport != config.port) {
|
||||||
close(server);
|
close(server);
|
||||||
server = server_create(config.port, config.bind_family);
|
server = server_create(config.port);
|
||||||
}
|
}
|
||||||
reload = 0;
|
reload = 0;
|
||||||
}
|
}
|
||||||
if (dumpstats) {
|
|
||||||
/* print stats requested (SIGUSR1) */
|
/* Enqueue the listening socket first */
|
||||||
statistics_log_totals(fifo->head);
|
pollvec_clear(pollvec);
|
||||||
dumpstats = 0;
|
if (queue->length < config.max_clients)
|
||||||
}
|
pollvec_push(pollvec, server, POLLIN);
|
||||||
|
else
|
||||||
|
pollvec_push(pollvec, -1, 0);
|
||||||
|
|
||||||
/* Enqueue clients that are due for another message */
|
/* Enqueue clients that are due for another message */
|
||||||
int timeout = -1;
|
int timeout = -1;
|
||||||
long long now = epochms();
|
long long now = uepoch();
|
||||||
while (fifo->head) {
|
for (struct client *c = queue->head; c; c = c->next) {
|
||||||
if (fifo->head->send_next <= now) {
|
if (c->send_next <= now) {
|
||||||
struct client *c = fifo_pop(fifo);
|
pollvec_push(pollvec, c->fd, POLLOUT);
|
||||||
if (sendline(c, config.max_line_length, &rng)) {
|
|
||||||
c->send_next = now + config.delay;
|
|
||||||
fifo_append(fifo, c);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
timeout = fifo->head->send_next - now;
|
timeout = c->send_next - now;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for next event */
|
/* Wait for next event */
|
||||||
struct pollfd fds = {server, POLLIN, 0};
|
logmsg(LOG_DEBUG, "poll(%zu, %d)%s", pollvec->fill, timeout,
|
||||||
int nfds = fifo->length < config.max_clients;
|
queue->length >= config.max_clients ? " (no accept)" : "");
|
||||||
logmsg(LOG_DEBUG, "poll(%d, %d)", nfds, timeout);
|
int r = poll(pollvec->fds, pollvec->fill, timeout);
|
||||||
int r = poll(&fds, nfds, timeout);
|
|
||||||
logmsg(LOG_DEBUG, "= %d", r);
|
logmsg(LOG_DEBUG, "= %d", r);
|
||||||
if (r == -1) {
|
if (r == -1) {
|
||||||
switch (errno) {
|
switch (errno) {
|
||||||
@@ -728,19 +686,18 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check for new incoming connections */
|
/* Check for new incoming connections */
|
||||||
if (fds.revents & POLLIN) {
|
if (pollvec->fds[0].revents & POLLIN) {
|
||||||
int fd = accept(server, 0, 0);
|
int fd = accept(server, 0, 0);
|
||||||
logmsg(LOG_DEBUG, "accept() = %d", fd);
|
logmsg(LOG_DEBUG, "accept() = %d", fd);
|
||||||
statistics.connects++;
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
const char *msg = strerror(errno);
|
const char *msg = strerror(errno);
|
||||||
switch (errno) {
|
switch (errno) {
|
||||||
case EMFILE:
|
case EMFILE:
|
||||||
case ENFILE:
|
case ENFILE:
|
||||||
config.max_clients = fifo->length;
|
config.max_clients = queue->length;
|
||||||
logmsg(LOG_INFO,
|
logmsg(LOG_INFO,
|
||||||
"MaxClients %d",
|
"MaxClients %d",
|
||||||
fifo->length);
|
queue->length);
|
||||||
break;
|
break;
|
||||||
case ECONNABORTED:
|
case ECONNABORTED:
|
||||||
case EINTR:
|
case EINTR:
|
||||||
@@ -754,22 +711,59 @@ main(int argc, char **argv)
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
long long send_next = epochms() + config.delay;
|
long long send_next = uepoch() + config.delay / 2;
|
||||||
struct client *client = client_new(fd, send_next);
|
struct client *client = client_new(fd, send_next);
|
||||||
int flags = fcntl(fd, F_GETFL, 0); /* cannot fail */
|
|
||||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK); /* cannot fail */
|
|
||||||
if (!client) {
|
if (!client) {
|
||||||
fprintf(stderr, "endlessh: warning: out of memory\n");
|
fprintf(stderr, "endlessh: warning: out of memory\n");
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
fifo_append(fifo, client);
|
queue_append(queue, client);
|
||||||
logmsg(LOG_INFO, "ACCEPT host=%s port=%d fd=%d n=%d/%d",
|
logmsg(LOG_INFO, "ACCEPT host=%s port=%d fd=%d n=%d/%d",
|
||||||
client->ipaddr, client->port, client->fd,
|
client->ipaddr, client->port, client->fd,
|
||||||
fifo->length, config.max_clients);
|
queue->length, config.max_clients);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write lines to ready clients */
|
||||||
|
for (size_t i = 1; i < pollvec->fill; i++) {
|
||||||
|
short fd = pollvec->fds[i].fd;
|
||||||
|
short revents = pollvec->fds[i].revents;
|
||||||
|
struct client *client = queue_remove(queue, fd);
|
||||||
|
|
||||||
|
if (revents & POLLHUP) {
|
||||||
|
client_destroy(client);
|
||||||
|
|
||||||
|
} else if (revents & POLLOUT) {
|
||||||
|
char line[256];
|
||||||
|
int len;
|
||||||
|
if (!(client->flags & FLAG_SPECIAL_SENT)) {
|
||||||
|
static const char special[] = SPECIAL_MESSAGE;
|
||||||
|
len = sizeof(special) - 1;
|
||||||
|
memcpy(line, special, len);
|
||||||
|
client->flags |= FLAG_SPECIAL_SENT;
|
||||||
|
} else {
|
||||||
|
len = randline(line, config.max_line_length, &rng);
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
/* Don't really care if send is short */
|
||||||
|
ssize_t out = send(fd, line, len, MSG_DONTWAIT);
|
||||||
|
if (out == -1 && errno == EINTR) {
|
||||||
|
continue; /* try again */
|
||||||
|
} else if (out == -1) {
|
||||||
|
client_destroy(client);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
logmsg(LOG_DEBUG, "send(%d) = %d", fd, (int)out);
|
||||||
|
client->bytes_sent += out;
|
||||||
|
client->send_next = uepoch() + config.delay;
|
||||||
|
queue_append(queue, client);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fifo_destroy(fifo);
|
pollvec_free(pollvec);
|
||||||
statistics_log_totals(0);
|
queue_destroy(queue);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/bin/ksh
|
|
||||||
#
|
|
||||||
|
|
||||||
daemon="/usr/local/bin/endlessh"
|
|
||||||
rc_bg=YES
|
|
||||||
|
|
||||||
. /etc/rc.d/rc.subr
|
|
||||||
|
|
||||||
rc_cmd $1
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
Solaris SMF installation
|
|
||||||
========================
|
|
||||||
|
|
||||||
Before installing SMF:
|
|
||||||
|
|
||||||
1. Put endlessh binary to /usr/local/bin
|
|
||||||
2. Edit endlessh.conf and put it to /usr/local/etc
|
|
||||||
|
|
||||||
To install SMF:
|
|
||||||
|
|
||||||
1. Put endlessh.xml to /var/svc/manifest/network
|
|
||||||
2. Run svccfg import endlessh.xml
|
|
||||||
3. Put init.endlessh to /lib/svc/method
|
|
||||||
4. Run svcadm enable endlessh
|
|
||||||
|
|
||||||
Note: Log will write to /var/log/endlessh.log by default.
|
|
||||||
|
|
||||||
To uninstall SMF:
|
|
||||||
|
|
||||||
1. Run svcadm disable endlessh
|
|
||||||
2. rm -f /lib/svc/method/init.endlessh
|
|
||||||
3. svccfg delete svc:/network/endlessh:default
|
|
||||||
4. rm -f /var/svc/manifest/network/endlessh.xml
|
|
||||||
|
|
||||||
Enjoy! :)
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# The port on which to listen for new SSH connections.
|
|
||||||
Port 22
|
|
||||||
|
|
||||||
# The endless banner is sent one line at a time. This is the delay
|
|
||||||
# in milliseconds between individual lines.
|
|
||||||
Delay 10000
|
|
||||||
|
|
||||||
# The length of each line is randomized. This controls the maximum
|
|
||||||
# length of each line. Shorter lines may keep clients on for longer if
|
|
||||||
# they give up after a certain number of bytes.
|
|
||||||
MaxLineLength 32
|
|
||||||
|
|
||||||
# Maximum number of connections to accept at a time. Connections beyond
|
|
||||||
# this are not immediately rejected, but will wait in the queue.
|
|
||||||
MaxClients 4096
|
|
||||||
|
|
||||||
# Set the detail level for the log.
|
|
||||||
# 0 = Quiet
|
|
||||||
# 1 = Standard, useful log messages
|
|
||||||
# 2 = Very noisy debugging information
|
|
||||||
LogLevel 1
|
|
||||||
|
|
||||||
# Set the family of the listening socket
|
|
||||||
# 0 = Use IPv4 Mapped IPv6 (Both v4 and v6, default)
|
|
||||||
# 4 = Use IPv4 only
|
|
||||||
# 6 = Use IPv6 only
|
|
||||||
BindFamily 0
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
|
|
||||||
<!-- Manifest-file for endlessh, put this file in
|
|
||||||
/var/svc/manifest/network/endlessh.xml
|
|
||||||
and run #svccfg import /var/svc/manifest/network/endlessh.xml
|
|
||||||
Fixed by Yuri Voinov (C) 2007,2019 -->
|
|
||||||
<service_bundle type='manifest' name='endlessh'>
|
|
||||||
|
|
||||||
<service
|
|
||||||
name='network/endlessh'
|
|
||||||
type='service'
|
|
||||||
version='1'>
|
|
||||||
|
|
||||||
<create_default_instance enabled='false' />
|
|
||||||
|
|
||||||
<single_instance />
|
|
||||||
|
|
||||||
<dependency name='fs-local'
|
|
||||||
grouping='require_all'
|
|
||||||
restart_on='none'
|
|
||||||
type='service'>
|
|
||||||
<service_fmri
|
|
||||||
value='svc:/system/filesystem/local' />
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency name='net-loopback'
|
|
||||||
grouping='require_all'
|
|
||||||
restart_on='none'
|
|
||||||
type='service'>
|
|
||||||
<service_fmri value='svc:/network/loopback' />
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency name='net-physical'
|
|
||||||
grouping='require_all'
|
|
||||||
restart_on='none'
|
|
||||||
type='service'>
|
|
||||||
<service_fmri value='svc:/network/physical' />
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency name='utmp'
|
|
||||||
grouping='require_all'
|
|
||||||
restart_on='none'
|
|
||||||
type='service'>
|
|
||||||
<service_fmri value='svc:/system/utmp' />
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency name='endlessh_config_data'
|
|
||||||
grouping='require_all'
|
|
||||||
restart_on='refresh'
|
|
||||||
type='path'>
|
|
||||||
<service_fmri value='file://localhost/usr/local/etc/endlessh.conf' />
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<exec_method
|
|
||||||
type='method'
|
|
||||||
name='start'
|
|
||||||
exec='/lib/svc/method/init.endlessh %m'
|
|
||||||
timeout_seconds='60'/>
|
|
||||||
|
|
||||||
<exec_method
|
|
||||||
type='method'
|
|
||||||
name='stop'
|
|
||||||
exec=':kill'
|
|
||||||
timeout_seconds='60' />
|
|
||||||
|
|
||||||
<exec_method
|
|
||||||
type='method'
|
|
||||||
name='restart'
|
|
||||||
exec='/lib/svc/method/init.endlessh %m'
|
|
||||||
timeout_seconds='60' />
|
|
||||||
|
|
||||||
<exec_method
|
|
||||||
type='method'
|
|
||||||
name='refresh'
|
|
||||||
exec='/lib/svc/method/init.endlessh %m'
|
|
||||||
timeout_seconds='60' />
|
|
||||||
|
|
||||||
<property_group name='general' type='framework'>
|
|
||||||
<!-- to start stop endlessh -->
|
|
||||||
<propval name='action_authorization' type='astring'
|
|
||||||
value='solaris.smf.manage' />
|
|
||||||
</property_group>
|
|
||||||
|
|
||||||
<stability value='Unstable' />
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<common_name>
|
|
||||||
<loctext xml:lang='C'>
|
|
||||||
endlessh service
|
|
||||||
</loctext>
|
|
||||||
</common_name>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</service>
|
|
||||||
|
|
||||||
</service_bundle>
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
#!/sbin/sh
|
|
||||||
|
|
||||||
#
|
|
||||||
# Control Method for endlessh (/lib/svc/method/init.endlessh)
|
|
||||||
# Written by Yuri Voinov (C) 2007,2019
|
|
||||||
#
|
|
||||||
# ident "@(#)endlessh.sh 1.8 19/27/03 YV"
|
|
||||||
#
|
|
||||||
|
|
||||||
#############
|
|
||||||
# Variables #
|
|
||||||
#############
|
|
||||||
|
|
||||||
# Base installation directory
|
|
||||||
BASE_DIR="/usr/local"
|
|
||||||
BASE_CONFIG_DIR=$BASE_DIR"/etc"
|
|
||||||
|
|
||||||
# endlessh files paths
|
|
||||||
ENDLESSH_PATH="$BASE_DIR""/bin"
|
|
||||||
ENDLESSH_CONF_PATH="$BASE_CONFIG_DIR"
|
|
||||||
|
|
||||||
# endlessh files
|
|
||||||
ENDLESSH_BIN_FILE="endlessh"
|
|
||||||
ENDLESSH_CONF_FILE=$ENDLESSH_BIN_FILE".conf"
|
|
||||||
|
|
||||||
# Daemon settings
|
|
||||||
ENDLESSH_CONF="$ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE"
|
|
||||||
|
|
||||||
# Log
|
|
||||||
LOG_DIR="/var/log"
|
|
||||||
LOGFILE=$LOG_DIR/$ENDLESSH_BIN_FILE".log"
|
|
||||||
|
|
||||||
#
|
|
||||||
# OS Commands location variables
|
|
||||||
#
|
|
||||||
CUT=`which cut`
|
|
||||||
ECHO=`which echo`
|
|
||||||
KILL=`which kill`
|
|
||||||
PGREP=`which pgrep`
|
|
||||||
UNAME=`which uname`
|
|
||||||
|
|
||||||
# OS release
|
|
||||||
OS_VER=`$UNAME -r|$CUT -f2 -d"."`
|
|
||||||
OS_NAME=`$UNAME -s|$CUT -f1 -d" "`
|
|
||||||
|
|
||||||
###############
|
|
||||||
# Subroutines #
|
|
||||||
###############
|
|
||||||
|
|
||||||
check_endlessh ()
|
|
||||||
{
|
|
||||||
# Check endlessh installed
|
|
||||||
program=$1
|
|
||||||
if [ ! -f "$ENDLESSH_PATH/$program" -a ! -x "$ENDLESSH_PATH/$program" ]; then
|
|
||||||
$ECHO "ERROR: endlessh not found!"
|
|
||||||
$ECHO "Exiting..."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_os ()
|
|
||||||
{
|
|
||||||
# Check OS version
|
|
||||||
if [ ! "$OS_NAME" = "SunOS" -a ! "$OS_VER" -lt "10" ]; then
|
|
||||||
$ECHO "ERROR: Unsupported OS $OS_NAME $OS_VER"
|
|
||||||
$ECHO "Exiting..."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
checkconf ()
|
|
||||||
{
|
|
||||||
# Check endlessh config file
|
|
||||||
config=$1
|
|
||||||
if [ -f "$ENDLESSH_CONF_PATH"/"$config" ]; then
|
|
||||||
$ECHO "1"
|
|
||||||
else
|
|
||||||
$ECHO "0"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
startproc()
|
|
||||||
{
|
|
||||||
# Start endlessh daemon
|
|
||||||
program=$1
|
|
||||||
if [ "`checkconf $ENDLESSH_CONF_FILE`" != "1" ]; then
|
|
||||||
$ECHO "ERROR: Config file $ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE not found."
|
|
||||||
$ECHO "Exiting..."
|
|
||||||
exit 2
|
|
||||||
else
|
|
||||||
$ENDLESSH_PATH/$program -f $ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE -v >$LOGFILE &
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
stopproc()
|
|
||||||
{
|
|
||||||
# Stop endlessh daemon
|
|
||||||
program=$1
|
|
||||||
if [ "`checkconf $ENDLESSH_CONF_FILE`" != "1" ]; then
|
|
||||||
$ECHO "ERROR: Config file $ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE not found."
|
|
||||||
$ECHO "Exiting..."
|
|
||||||
exit 2
|
|
||||||
else
|
|
||||||
$KILL -s TERM `$PGREP $program`>/dev/null 2>&1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
##############
|
|
||||||
# Main block #
|
|
||||||
##############
|
|
||||||
|
|
||||||
# Check endlessh installed
|
|
||||||
check_endlessh $ENDLESSH_BIN_FILE
|
|
||||||
|
|
||||||
# Check OS version
|
|
||||||
check_os
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
"start")
|
|
||||||
startproc $ENDLESSH_BIN_FILE
|
|
||||||
;;
|
|
||||||
"stop")
|
|
||||||
stopproc $ENDLESSH_BIN_FILE
|
|
||||||
;;
|
|
||||||
"refresh")
|
|
||||||
$KILL -s HUP `$PGREP $ENDLESSH_BIN_FILE`>/dev/null 2>&1
|
|
||||||
;;
|
|
||||||
"restart")
|
|
||||||
stopproc $ENDLESSH_BIN_FILE
|
|
||||||
startproc $ENDLESSH_BIN_FILE
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
$ECHO "Usage: $0 { start | stop | restart | refresh }"
|
|
||||||
exit 1
|
|
||||||
esac
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
Reference in New Issue
Block a user