logger: permit to send messages larger than 1024 characters

This is an important capability that has been specified in RFC5424.
However, messages larger than 1024 chars are being accepted for years
now by at least rsyslog and syslog-ng.

This patch adds the option --size to permit setting a new max
size, with 1024 being the default.

Note that the size limit is only approximative, as we do not take the
header size in account (RFC talks about total message length).

[[kzak@redhat.com: - add 'S' to getopt_long(),
                   - rename --message-size to --size
                   - add the option to bash-completion]

Signed-off-by: Karel Zak <kzak@redhat.com>
diff --git a/bash-completion/logger b/bash-completion/logger
index 0a66af3..593a678 100644
--- a/bash-completion/logger
+++ b/bash-completion/logger
@@ -37,7 +37,7 @@
 	esac
 	case $cur in
 		-*)
-			OPTS="--journald --udp --id --file --help --server --port --priority --rfc3164 --rfc5424 --stderr --tag --socket --version"
+			OPTS="--journald --udp --id --file --help --server --port --priority --rfc3164 --rfc5424 --stderr --tag --size --socket --version"
 			COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
 			return 0
 			;;
diff --git a/misc-utils/logger.1 b/misc-utils/logger.1
index e05e99f..b73f46a 100644
--- a/misc-utils/logger.1
+++ b/misc-utils/logger.1
@@ -86,6 +86,21 @@
 will display MESSAGE field.  Use
 .B journalctl --output json-pretty
 to see rest of the fields.
+.TP
+.BR \-\--size " \fIsize
+Sets the maximum permitted message size to \fIsize\fR. The default
+is 1KiB characters, which is the limit traditionally used and specified
+in RFC 3164. With RFC 5424, this limit has become flexible. A good assumption
+is that RFC 5424 receivers can at least process 4KiB messages.
+
+Most receivers accept larger than 1KiB message over any type of syslog
+protocol. As such, the \fB\-\-size\fR option affects logger in
+all cases (not only when \fB\-\-rfc5424\fR was used).
+
+Note: the message size limit is not totally strict. It limits only
+the user data, the size of the syslog header is not considered. While
+this can lead to some truncation at the receiver level, a consistent limit
+based on the user data size is generally wanted.
 
 .TP
 .BR \-n , " \-\-server " \fIserver
diff --git a/misc-utils/logger.c b/misc-utils/logger.c
index 24cbb3b..3740c2e 100644
--- a/misc-utils/logger.c
+++ b/misc-utils/logger.c
@@ -102,6 +102,7 @@
 	char *server;
 	char *port;
 	int socket_type;
+	size_t max_message_size;
 	void (*syslogfp)(const struct logger_ctl *ctl, const char *msg);
 	unsigned int
 			unix_socket_errors:1,	/* whether to report or not errors */
@@ -374,7 +375,7 @@
 	if (dot)
 		*dot = '\0';
 
-	len = xasprintf(&buf, "<%d>%.15s %s %.200s%s: %.400s",
+	len = xasprintf(&buf, "<%d>%.15s %s %.200s%s: %s",
 		 ctl->pri, rfc3164_current_time(), hostname, cp, pid, msg);
 
 	write_output(ctl, buf, len);
@@ -522,9 +523,9 @@
 
 static void logger_command_line(const struct logger_ctl *ctl, char **argv)
 {
-	char buf[4096];
+	char *const buf = xmalloc(ctl->max_message_size + 1);
 	char *p = buf;
-	const char *endp = buf + sizeof(buf) - 2;
+	const char *endp = buf + ctl->max_message_size - 1;
 	size_t len;
 
 	while (*argv) {
@@ -533,7 +534,8 @@
 			ctl->syslogfp(ctl, buf);
 			p = buf;
 		}
-		if (sizeof(buf) - 1 < len) {
+		if (ctl->max_message_size < len) {
+			(*argv)[ctl->max_message_size] = '\0'; /* truncate */
 			ctl->syslogfp(ctl, *argv++);
 			continue;
 		}
@@ -550,9 +552,9 @@
 {
 	char *msg;
 	int default_priority = ctl->pri;
-	char buf[1024];
+	char *const buf = xmalloc(ctl->max_message_size + 2);
 
-	while (fgets(buf, sizeof(buf), stdin) != NULL) {
+	while (fgets(buf, ctl->max_message_size+2, stdin) != NULL) {
 		int len = strlen(buf);
 
 		/* some glibc versions are buggy, they add an additional
@@ -588,6 +590,7 @@
 	fputs(_(" -p, --priority <prio>    mark given message with this priority\n"), out);
 	fputs(_("     --prio-prefix        look for a prefix on every line read from stdin\n"), out);
 	fputs(_(" -s, --stderr             output message to standard error as well\n"), out);
+	fputs(_(" -S, --size <size>        maximum size for a single message\n"), out);
 	fputs(_(" -t, --tag <tag>          mark every line with this tag\n"), out);
 	fputs(_(" -n, --server <name>      write to this remote syslog server\n"), out);
 	fputs(_(" -P, --port <number>      use this UDP port\n"), out);
@@ -630,6 +633,7 @@
 		.server = NULL,
 		.port = NULL,
 		.socket_type = ALL_TYPES,
+		.max_message_size = 1024,
 		.rfc5424_time = 1,
 		.rfc5424_tq = 1,
 		.rfc5424_host = 1,
@@ -657,6 +661,7 @@
 		{ "prio-prefix", no_argument, 0, OPT_PRIO_PREFIX },
 		{ "rfc3164",	no_argument,  0, OPT_RFC3164 },
 		{ "rfc5424",	optional_argument,  0, OPT_RFC5424 },
+		{ "size",       required_argument,  0, 'S' },
 #ifdef HAVE_LIBSYSTEMD
 		{ "journald",   optional_argument,  0, OPT_JOURNALD },
 #endif
@@ -668,7 +673,7 @@
 	textdomain(PACKAGE);
 	atexit(close_stdout);
 
-	while ((ch = getopt_long(argc, argv, "f:ip:st:u:dTn:P:Vh",
+	while ((ch = getopt_long(argc, argv, "f:ip:S:st:u:dTn:P:Vh",
 					    longopts, NULL)) != -1) {
 		switch (ch) {
 		case 'f':		/* file to log */
@@ -701,6 +706,10 @@
 		case 'u':		/* unix socket */
 			ctl.unix_socket = optarg;
 			break;
+		case 'S':		/* max message size */
+			ctl.max_message_size = strtosize_or_err(optarg,
+				_("failed to parse message size"));
+			break;
 		case 'd':
 			ctl.socket_type = TYPE_UDP;
 			break;