Skip to content

Commit c4463ec

Browse files
committed
initctl: escape special characters in JSON output
Strings like command, description, and environment may contain characters that need escaping for valid JSON, e.g., embedded quotes in command line arguments like -V "NanoPi R2S". Add json_escape() helper to handle quotes, backslashes, and control chars. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
1 parent 6f18082 commit c4463ec

1 file changed

Lines changed: 69 additions & 9 deletions

File tree

src/initctl.c

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,62 @@ static int svc_compare(svc_t *svc, char *arg)
10361036
return 0;
10371037
}
10381038

1039+
/*
1040+
* Escape a string for safe JSON output. Handles quotes, backslashes,
1041+
* and control characters. Returns pointer to static buffer.
1042+
*/
1043+
static char *json_escape(const char *str)
1044+
{
1045+
static char buf[1024];
1046+
char *ptr = buf;
1047+
size_t left = sizeof(buf) - 1;
1048+
1049+
if (!str)
1050+
return "";
1051+
1052+
while (*str && left > 1) {
1053+
char c = *str++;
1054+
1055+
switch (c) {
1056+
case '"':
1057+
case '\\':
1058+
*ptr++ = '\\';
1059+
*ptr++ = c;
1060+
left -= 2;
1061+
break;
1062+
case '\n':
1063+
*ptr++ = '\\';
1064+
*ptr++ = 'n';
1065+
left -= 2;
1066+
break;
1067+
case '\r':
1068+
*ptr++ = '\\';
1069+
*ptr++ = 'r';
1070+
left -= 2;
1071+
break;
1072+
case '\t':
1073+
*ptr++ = '\\';
1074+
*ptr++ = 't';
1075+
left -= 2;
1076+
break;
1077+
default:
1078+
if ((unsigned char)c < 0x20) {
1079+
/* Other control chars: use \uXXXX */
1080+
int n = snprintf(ptr, left, "\\u%04x", (unsigned char)c);
1081+
ptr += n;
1082+
left -= n;
1083+
} else {
1084+
*ptr++ = c;
1085+
left--;
1086+
}
1087+
break;
1088+
}
1089+
}
1090+
*ptr = '\0';
1091+
1092+
return buf;
1093+
}
1094+
10391095
static int json_status_one(FILE *fp, svc_t *svc, char *indent, int prev)
10401096
{
10411097
long now = jiffies();
@@ -1051,14 +1107,16 @@ static int json_status_one(FILE *fp, svc_t *svc, char *indent, int prev)
10511107
fprintf(fp,
10521108
"%s"
10531109
"%s{\n"
1054-
"%s \"identity\": \"%s\",\n"
1055-
"%s \"description\": \"%s\",\n"
1110+
"%s \"identity\": \"%s\",\n",
1111+
prev ? ",\n" : indent, prev ? indent : "",
1112+
indent, svc_ident(svc, NULL, 0));
1113+
fprintf(fp,
1114+
"%s \"description\": \"%s\",\n",
1115+
indent, json_escape(svc->desc));
1116+
fprintf(fp,
10561117
"%s \"type\": \"%s\",\n"
10571118
"%s \"forking\": %s,\n"
10581119
"%s \"status\": \"%s\",\n",
1059-
prev ? ",\n" : indent, prev ? indent : "",
1060-
indent, svc_ident(svc, NULL, 0),
1061-
indent, svc->desc,
10621120
indent, svc_typestr(svc),
10631121
indent, svc->forking ? "true" : "false",
10641122
indent, svc_status(svc));
@@ -1080,15 +1138,17 @@ static int json_status_one(FILE *fp, svc_t *svc, char *indent, int prev)
10801138
}
10811139

10821140
fprintf(fp,
1083-
"%s \"origin\": \"%s\",\n"
1141+
"%s \"origin\": \"%s\",\n",
1142+
indent, svc->file[0] ? svc->file : "built-in");
1143+
svc_command(svc, buf, sizeof(buf), 0);
1144+
fprintf(fp,
10841145
"%s \"command\": \"%s\",\n",
1085-
indent, svc->file[0] ? svc->file : "built-in",
1086-
indent, svc_command(svc, buf, sizeof(buf), 0));
1146+
indent, json_escape(buf));
10871147

10881148
svc_environ(svc, buf, sizeof(buf), 0);
10891149
if (buf[0])
10901150
fprintf(fp,
1091-
"%s \"environment\": \"%s\",\n", indent, buf);
1151+
"%s \"environment\": \"%s\",\n", indent, json_escape(buf));
10921152

10931153
svc_cond(svc, buf, sizeof(buf), 0);
10941154
if (buf[0])

0 commit comments

Comments
 (0)