summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/basic/fileio.c25
-rw-r--r--src/basic/parse-util.c18
-rw-r--r--src/basic/parse-util.h2
-rw-r--r--src/basic/process-util.h5
-rw-r--r--src/core/automount.c3
-rw-r--r--src/core/dbus-automount.c54
-rw-r--r--src/core/dbus-automount.h2
-rw-r--r--src/core/dbus-execute.c2
-rw-r--r--src/core/dbus-mount.c3
-rw-r--r--src/core/load-fragment.c32
-rw-r--r--src/core/main.c1
-rw-r--r--src/core/mount.c34
-rw-r--r--src/journal-remote/journal-gatewayd.c3
-rw-r--r--src/journal-remote/journal-remote.c4
-rw-r--r--src/network/networkd-brvlan.c20
-rw-r--r--src/network/networkd-brvlan.h1
-rw-r--r--src/network/networkd-link.c5
-rw-r--r--src/network/networkd-network-gperf.gperf2
-rw-r--r--src/network/networkd-network.h1
-rw-r--r--src/run/run.c8
-rw-r--r--src/shared/bus-unit-util.c12
-rw-r--r--src/test/test-parse-util.c29
22 files changed, 199 insertions, 67 deletions
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index f183de4999..588eacd77c 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -47,6 +47,8 @@
#include "umask-util.h"
#include "utf8.h"
+#define READ_FULL_BYTES_MAX (4U*1024U*1024U)
+
int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
assert(f);
@@ -230,7 +232,7 @@ int read_full_stream(FILE *f, char **contents, size_t *size) {
if (S_ISREG(st.st_mode)) {
/* Safety check */
- if (st.st_size > 4*1024*1024)
+ if (st.st_size > READ_FULL_BYTES_MAX)
return -E2BIG;
/* Start with the right file size, but be prepared for
@@ -245,26 +247,31 @@ int read_full_stream(FILE *f, char **contents, size_t *size) {
char *t;
size_t k;
- t = realloc(buf, n+1);
+ t = realloc(buf, n + 1);
if (!t)
return -ENOMEM;
buf = t;
k = fread(buf + l, 1, n - l, f);
+ if (k > 0)
+ l += k;
- if (k <= 0) {
- if (ferror(f))
- return -errno;
+ if (ferror(f))
+ return -errno;
+ if (feof(f))
break;
- }
- l += k;
- n *= 2;
+ /* We aren't expecting fread() to return a short read outside
+ * of (error && eof), assert buffer is full and enlarge buffer.
+ */
+ assert(l == n);
/* Safety check */
- if (n > 4*1024*1024)
+ if (n >= READ_FULL_BYTES_MAX)
return -E2BIG;
+
+ n = MIN(n * 2, READ_FULL_BYTES_MAX);
}
buf[l] = 0;
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index 11849ade0b..c98815b9bc 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -29,6 +29,7 @@
#include "extract-word.h"
#include "macro.h"
#include "parse-util.h"
+#include "process-util.h"
#include "string-util.h"
int parse_boolean(const char *v) {
@@ -551,10 +552,25 @@ int parse_percent_unbounded(const char *p) {
}
int parse_percent(const char *p) {
- int v = parse_percent_unbounded(p);
+ int v;
+ v = parse_percent_unbounded(p);
if (v > 100)
return -ERANGE;
return v;
}
+
+int parse_nice(const char *p, int *ret) {
+ int n, r;
+
+ r = safe_atoi(p, &n);
+ if (r < 0)
+ return r;
+
+ if (!nice_is_valid(n))
+ return -ERANGE;
+
+ *ret = n;
+ return 0;
+}
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index f0fa5f9752..461e1cd4d8 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -108,3 +108,5 @@ int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
int parse_percent_unbounded(const char *p);
int parse_percent(const char *p);
+
+int parse_nice(const char *p, int *ret);
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index 9f75088796..2568e3834f 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
+#include <sys/resource.h>
#include "formats-util.h"
#include "macro.h"
@@ -103,3 +104,7 @@ int sched_policy_from_string(const char *s);
void valgrind_summary_hack(void);
int pid_compare_func(const void *a, const void *b);
+
+static inline bool nice_is_valid(int n) {
+ return n >= PRIO_MIN && n < PRIO_MAX;
+}
diff --git a/src/core/automount.c b/src/core/automount.c
index 4e9891569c..20a73c76f9 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -1105,6 +1105,9 @@ const UnitVTable automount_vtable = {
.reset_failed = automount_reset_failed,
.bus_vtable = bus_automount_vtable,
+ .bus_set_property = bus_automount_set_property,
+
+ .can_transient = true,
.shutdown = automount_shutdown,
.supported = automount_supported,
diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c
index b2806ad86f..26212b3a95 100644
--- a/src/core/dbus-automount.c
+++ b/src/core/dbus-automount.c
@@ -32,3 +32,57 @@ const sd_bus_vtable bus_automount_vtable[] = {
SD_BUS_PROPERTY("TimeoutIdleUSec", "t", bus_property_get_usec, offsetof(Automount, timeout_idle_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
+
+static int bus_automount_set_transient_property(
+ Automount *a,
+ const char *name,
+ sd_bus_message *message,
+ UnitSetPropertiesMode mode,
+ sd_bus_error *error) {
+
+ int r;
+
+ assert(a);
+ assert(name);
+ assert(message);
+
+ if (streq(name, "TimeoutIdleUSec")) {
+ usec_t timeout_idle_usec;
+ r = sd_bus_message_read(message, "t", &timeout_idle_usec);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ char time[FORMAT_TIMESPAN_MAX];
+
+ a->timeout_idle_usec = timeout_idle_usec;
+ unit_write_drop_in_format(UNIT(a), mode, name, "[Automount]\nTimeoutIdleSec=%s\n",
+ format_timespan(time, sizeof(time), timeout_idle_usec, USEC_PER_MSEC));
+ }
+ } else
+ return 0;
+
+ return 1;
+}
+
+int bus_automount_set_property(
+ Unit *u,
+ const char *name,
+ sd_bus_message *message,
+ UnitSetPropertiesMode mode,
+ sd_bus_error *error) {
+
+ Automount *a = AUTOMOUNT(u);
+ int r = 0;
+
+ assert(a);
+ assert(name);
+ assert(message);
+
+ if (u->transient && u->load_state == UNIT_STUB)
+ /* This is a transient unit, let's load a little more */
+
+ r = bus_automount_set_transient_property(a, name, message, mode, error);
+
+ return r;
+}
diff --git a/src/core/dbus-automount.h b/src/core/dbus-automount.h
index 7b51eb973a..f41adda2a6 100644
--- a/src/core/dbus-automount.h
+++ b/src/core/dbus-automount.h
@@ -21,3 +21,5 @@
extern const sd_bus_vtable bus_automount_vtable[];
+
+int bus_automount_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error);
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 9c50cd93e5..346c8b973e 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -935,7 +935,7 @@ int bus_exec_context_set_transient_property(
if (r < 0)
return r;
- if (n < PRIO_MIN || n >= PRIO_MAX)
+ if (!nice_is_valid(n))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Nice value out of range");
if (mode != UNIT_CHECK) {
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index 935db7c48b..b4bbee0648 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -157,6 +157,9 @@ static int bus_mount_set_transient_property(
if (!p)
return -ENOMEM;
+ unit_write_drop_in_format(UNIT(m), mode, name, "[Mount]\n%s=%s\n",
+ name, new_property);
+
free(*property);
*property = p;
}
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index d5f035b67f..420f368689 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -491,16 +491,17 @@ int config_parse_socket_bind(const char *unit,
return 0;
}
-int config_parse_exec_nice(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_nice(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
int priority, r;
@@ -510,14 +511,13 @@ int config_parse_exec_nice(const char *unit,
assert(rvalue);
assert(data);
- r = safe_atoi(rvalue, &priority);
+ r = parse_nice(rvalue, &priority);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue);
- return 0;
- }
+ if (r == -ERANGE)
+ log_syntax(unit, LOG_ERR, filename, line, r, "Nice priority out of range, ignoring: %s", rvalue);
+ else
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue);
- if (priority < PRIO_MIN || priority >= PRIO_MAX) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Nice priority out of range, ignoring: %s", rvalue);
return 0;
}
diff --git a/src/core/main.c b/src/core/main.c
index 094bbef964..02324d325e 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1614,6 +1614,7 @@ int main(int argc, char *argv[]) {
retval = version();
goto finish;
} else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) {
+ pager_open(arg_no_pager, false);
unit_dump_config_items(stdout);
retval = EXIT_SUCCESS;
goto finish;
diff --git a/src/core/mount.c b/src/core/mount.c
index db5cafcb11..afb20af9e2 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -484,6 +484,7 @@ static int mount_add_default_dependencies(Mount *m) {
static int mount_verify(Mount *m) {
_cleanup_free_ char *e = NULL;
+ MountParameters *p;
int r;
assert(m);
@@ -508,7 +509,8 @@ static int mount_verify(Mount *m) {
return -EINVAL;
}
- if (UNIT(m)->fragment_path && !m->parameters_fragment.what) {
+ p = get_mount_parameters_fragment(m);
+ if (p && !p->what) {
log_unit_error(UNIT(m), "What= setting is missing. Refusing.");
return -EBADMSG;
}
@@ -862,11 +864,6 @@ fail:
mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
}
-static int mount_get_opts(Mount *m, char **ret) {
- return fstab_filter_options(m->parameters_fragment.options,
- "nofail\0" "noauto\0" "auto\0", NULL, NULL, ret);
-}
-
static void mount_enter_mounting(Mount *m) {
int r;
MountParameters *p;
@@ -889,19 +886,18 @@ static void mount_enter_mounting(Mount *m) {
if (p && mount_is_bind(p))
(void) mkdir_p_label(p->what, m->directory_mode);
- if (m->from_fragment) {
+ if (p) {
_cleanup_free_ char *opts = NULL;
- r = mount_get_opts(m, &opts);
+ r = fstab_filter_options(p->options, "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts);
if (r < 0)
goto fail;
- r = exec_command_set(m->control_command, MOUNT_PATH,
- m->parameters_fragment.what, m->where, NULL);
+ r = exec_command_set(m->control_command, MOUNT_PATH, p->what, m->where, NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
- if (r >= 0 && m->parameters_fragment.fstype)
- r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL);
+ if (r >= 0 && p->fstype)
+ r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
if (r >= 0 && !isempty(opts))
r = exec_command_append(m->control_command, "-o", opts, NULL);
} else
@@ -927,27 +923,29 @@ fail:
static void mount_enter_remounting(Mount *m) {
int r;
+ MountParameters *p;
assert(m);
m->control_command_id = MOUNT_EXEC_REMOUNT;
m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT;
- if (m->from_fragment) {
+ p = get_mount_parameters_fragment(m);
+ if (p) {
const char *o;
- if (m->parameters_fragment.options)
- o = strjoina("remount,", m->parameters_fragment.options);
+ if (p->options)
+ o = strjoina("remount,", p->options);
else
o = "remount";
r = exec_command_set(m->control_command, MOUNT_PATH,
- m->parameters_fragment.what, m->where,
+ p->what, m->where,
"-o", o, NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
- if (r >= 0 && m->parameters_fragment.fstype)
- r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL);
+ if (r >= 0 && p->fstype)
+ r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
} else
r = -ENOENT;
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 4ad9184993..a1627fab39 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -19,9 +19,6 @@
#include <fcntl.h>
#include <getopt.h>
-#ifdef HAVE_GNUTLS
-#include <gnutls/gnutls.h>
-#endif
#include <microhttpd.h>
#include <stdlib.h>
#include <string.h>
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index f1ef90ed7a..80e2adb100 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -27,10 +27,6 @@
#include <sys/socket.h>
#include <unistd.h>
-#ifdef HAVE_GNUTLS
-#include <gnutls/gnutls.h>
-#endif
-
#include "sd-daemon.h"
#include "alloc-util.h"
diff --git a/src/network/networkd-brvlan.c b/src/network/networkd-brvlan.c
index 8bc330ebae..18ecd86858 100644
--- a/src/network/networkd-brvlan.c
+++ b/src/network/networkd-brvlan.c
@@ -257,6 +257,24 @@ static int parse_vid_range(const char *rvalue, uint16_t *vid, uint16_t *vid_end)
return r;
}
+int config_parse_brvlan_pvid(const char *unit, const char *filename,
+ unsigned line, const char *section,
+ unsigned section_line, const char *lvalue,
+ int ltype, const char *rvalue, void *data,
+ void *userdata) {
+ Network *network = userdata;
+ int r;
+ uint16_t pvid;
+ r = parse_vlanid(rvalue, &pvid);
+ if (r < 0)
+ return r;
+
+ network->pvid = pvid;
+ network->use_br_vlan = true;
+
+ return 0;
+}
+
int config_parse_brvlan_vlan(const char *unit, const char *filename,
unsigned line, const char *section,
unsigned section_line, const char *lvalue,
@@ -288,6 +306,7 @@ int config_parse_brvlan_vlan(const char *unit, const char *filename,
for (; vid <= vid_end; vid++)
set_bit(vid, network->br_vid_bitmap);
}
+ network->use_br_vlan = true;
return 0;
}
@@ -325,5 +344,6 @@ int config_parse_brvlan_untagged(const char *unit, const char *filename,
set_bit(vid, network->br_untagged_bitmap);
}
}
+ network->use_br_vlan = true;
return 0;
}
diff --git a/src/network/networkd-brvlan.h b/src/network/networkd-brvlan.h
index 6aa6883bfc..b37633f94f 100644
--- a/src/network/networkd-brvlan.h
+++ b/src/network/networkd-brvlan.h
@@ -25,5 +25,6 @@ typedef struct Link Link;
int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap);
+int config_parse_brvlan_pvid(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_brvlan_vlan(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_brvlan_untagged(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 3e10ab1e04..69ee7424ce 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1357,7 +1357,7 @@ static int link_set_flags(Link *link) {
if (link->network->arp >= 0) {
ifi_change |= IFF_NOARP;
- ifi_flags |= IFF_NOARP;
+ ifi_flags |= link->network->arp ? 0 : IFF_NOARP;
}
r = sd_rtnl_message_link_set_flags(req, ifi_flags, ifi_change);
@@ -2060,7 +2060,8 @@ static int link_joined(Link *link) {
log_link_error_errno(link, r, "Could not set bridge message: %m");
}
- if (link->network->bridge || streq_ptr("bridge", link->kind)) {
+ if (link->network->use_br_vlan &&
+ (link->network->bridge || streq_ptr("bridge", link->kind))) {
r = link_set_bridge_vlan(link);
if (r < 0)
log_link_error_errno(link, r, "Could not set bridge vlan: %m");
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 19adac66b8..b96f0b7210 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -115,7 +115,7 @@ Bridge.AllowPortToBeRoot, config_parse_bool,
Bridge.UnicastFlood, config_parse_bool, 0, offsetof(Network, unicast_flood)
BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0
BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0
-BridgeVLAN.PVID, config_parse_vlanid, 0, offsetof(Network, pvid)
+BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0
BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0
BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0
/* backwards compatibility: do not add new entries to this section */
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 7c0bdc1e4a..5460eb4d1c 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -151,6 +151,7 @@ struct Network {
bool unicast_flood;
unsigned cost;
+ bool use_br_vlan;
uint16_t pvid;
uint32_t br_vid_bitmap[BRIDGE_VLAN_BITMAP_LEN];
uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN];
diff --git a/src/run/run.c b/src/run/run.c
index 58fa49a4d1..1917ffd857 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -257,11 +257,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NICE:
- r = safe_atoi(optarg, &arg_nice);
- if (r < 0 || arg_nice < PRIO_MIN || arg_nice >= PRIO_MAX) {
- log_error("Failed to parse nice value");
- return -EINVAL;
- }
+ r = parse_nice(optarg, &arg_nice);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse nice value: %s", optarg);
arg_nice_set = true;
break;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 589f9d46e9..c3a5233532 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -366,15 +366,13 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
}
} else if (streq(field, "Nice")) {
- int32_t i;
+ int n;
- r = safe_atoi32(eq, &i);
- if (r < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
+ r = parse_nice(eq, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse nice value: %s", eq);
- r = sd_bus_message_append(m, "v", "i", i);
+ r = sd_bus_message_append(m, "v", "i", (int32_t) n);
} else if (STR_IN_SET(field, "Environment", "PassEnvironment")) {
const char *p;
diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c
index 097c464229..d08014100b 100644
--- a/src/test/test-parse-util.c
+++ b/src/test/test-parse-util.c
@@ -498,6 +498,34 @@ static void test_parse_percent_unbounded(void) {
assert_se(parse_percent_unbounded("400%") == 400);
}
+static void test_parse_nice(void) {
+ int n;
+
+ assert_se(parse_nice("0", &n) >= 0 && n == 0);
+ assert_se(parse_nice("+0", &n) >= 0 && n == 0);
+ assert_se(parse_nice("-1", &n) >= 0 && n == -1);
+ assert_se(parse_nice("-2", &n) >= 0 && n == -2);
+ assert_se(parse_nice("1", &n) >= 0 && n == 1);
+ assert_se(parse_nice("2", &n) >= 0 && n == 2);
+ assert_se(parse_nice("+1", &n) >= 0 && n == 1);
+ assert_se(parse_nice("+2", &n) >= 0 && n == 2);
+ assert_se(parse_nice("-20", &n) >= 0 && n == -20);
+ assert_se(parse_nice("19", &n) >= 0 && n == 19);
+ assert_se(parse_nice("+19", &n) >= 0 && n == 19);
+
+
+ assert_se(parse_nice("", &n) == -EINVAL);
+ assert_se(parse_nice("-", &n) == -EINVAL);
+ assert_se(parse_nice("+", &n) == -EINVAL);
+ assert_se(parse_nice("xx", &n) == -EINVAL);
+ assert_se(parse_nice("-50", &n) == -ERANGE);
+ assert_se(parse_nice("50", &n) == -ERANGE);
+ assert_se(parse_nice("+50", &n) == -ERANGE);
+ assert_se(parse_nice("-21", &n) == -ERANGE);
+ assert_se(parse_nice("20", &n) == -ERANGE);
+ assert_se(parse_nice("+20", &n) == -ERANGE);
+}
+
int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
@@ -513,6 +541,7 @@ int main(int argc, char *argv[]) {
test_safe_atod();
test_parse_percent();
test_parse_percent_unbounded();
+ test_parse_nice();
return 0;
}