Re: OpenBSD 7.2 on Oracle Cloud
On Tue, May 2, 2023 at 11:30 AM Aaron Mason wrote: > > On Tue, May 2, 2023 at 9:29 AM Aaron Mason wrote: > > [REDACTED] > > > > > > The actual problem is here. One request times out, but the driver does > > > not tell qemu that it should abort the request. The queue entry then > > > gets reused and the two responses from qemu overwrite each other. You > > > could try if increasing the timeout here to e.g. 1 helps: > > > > > > if (ISSET(xs->flags, SCSI_POLL)) { > > > DPRINTF("vioscsi_scsi_cmd: polling...\n"); > > > int timeout = 1000; > > > do { > > > virtio_poll_intr(vsc); > > > if (vr->vr_xs != xs) > > > break; > > > delay(1000); > > > } while (--timeout > 0); > > > if (vr->vr_xs == xs) { > > > // TODO(matthew): Abort the request. > > > xs->error = XS_TIMEOUT; > > > xs->resid = xs->datalen; > > > DPRINTF("vioscsi_scsi_cmd: polling timeout\n"); > > > scsi_done(xs); > > > } > > > > > > Unfortunately, it order to properly abort the request, quite a bit of > > > infrastructure related to the control queue is still missing in the > > > driver. > > > > I'll give it a go and report back, thanks! > > > > No dice, it only takes longer to generate a page fault. > > -- > Aaron Mason - Programmer, open source addict > I've taken my software vows - for beta or for worse Increasing it to 4 has the same effect, only longer. -- Aaron Mason - Programmer, open source addict I've taken my software vows - for beta or for worse
Re: usr.bin/mail: cmd3.c:bangexp(): "borked"?, and BSD fails POSIX compat
Steffen Nurpmeso wrote in <20230502214014._ziz6%stef...@sdaoden.eu>: |Hallo, and sorry for the cross-post, but so all in one (maybe) go. | |This is about a niche "feature" of mail, the shell command "bang" |(! / ~! in compose mode): ... |POSIX now says / will say | | If the bang variable is set, each unescaped occurrence of '!' in | command shall be replaced with the command executed by the | previous ! command or ˜! command escape. | |thus | | - All commands entered for ! and ~! shall be stored. | | - If "bang" is set, an unquoted ! shall be replaced by that |storage. ... |It will fail to properly "bang" a second time[.] Eh, forget this please. Of course not, one simply inserts the old string without expansion. Since vim(1) offers the same "bang"ing, and merges \! to ! (source comment speaks of vi-compat) i keep it. (It actually requires '\!' or \\! ro avoid its expansion in ":echo BLA".) Ciao. static struct str last_bang; struct n_string xbang, *bang; char c; char const *cp_orig; boole bangit, changed; NYD_IN; bangit = ok_blook(bang); changed = FAL0; cp_orig = cp; for(bang = n_string_creat(); (c = *cp++) != '\0';){ if(bangit && c == '!'){ changed = TRU1; if(last_bang.l > 0) bang = n_string_push_buf(bang, last_bang.s, last_bang.l); }else{ if(c == '\\' && *cp == '!'){ changed = TRU1; ++cp; c = '!'; } bang = n_string_push_c(bang, c); } } if(last_bang.s != NIL) su_FREE(last_bang.s); last_bang.s = n_string_cp(bang); last_bang.l = bang->s_len; bang = n_string_drop_ownership(bang); n_string_gut(bang); if(bangit){ cp = last_bang.s; if(changed && (n_psonce & n_PSO_INTERACTIVE)) fprintf(n_stdout, "!%s\n", cp); }else cp = cp_orig; NYD_OU; return cp; --steffen | |Der Kragenbaer,The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) |~~ |..and in spring, hear David Leonard sing.. | |The black bear, The black bear, |blithely holds his own holds himself at leisure |beating it, up and down tossing over his ups and downs with pleasure |~~ |Farewell, dear collar bear
nd6 less kernel lock
Hi, Some checks in nd6_resolve() do not require kernel lock. The analog code for ARP has been unlocked in if_ether.c revision 1.250 since 2022/06/27 20:47:10. ok? bluhm Index: netinet6/nd6.c === RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/nd6.c,v retrieving revision 1.273 diff -u -p -r1.273 nd6.c --- netinet6/nd6.c 2 May 2023 06:06:13 - 1.273 +++ netinet6/nd6.c 2 May 2023 18:44:23 - @@ -1260,15 +1260,12 @@ nd6_resolve(struct ifnet *ifp, struct rt return (0); } - /* XXXSMP there is a MP race in nd6_resolve() */ - KERNEL_LOCK(); uptime = getuptime(); rt = rt_getll(rt0); if (ISSET(rt->rt_flags, RTF_REJECT) && (rt->rt_expire == 0 || rt->rt_expire > uptime)) { m_freem(m); - KERNEL_UNLOCK(); return (rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); } @@ -1291,6 +1288,11 @@ nd6_resolve(struct ifnet *ifp, struct rt goto bad; } + KERNEL_LOCK(); + if (!ISSET(rt->rt_flags, RTF_LLINFO)) { + KERNEL_UNLOCK(); + goto bad; + } ln = (struct llinfo_nd6 *)rt->rt_llinfo; KASSERT(ln != NULL); @@ -1321,6 +1323,8 @@ nd6_resolve(struct ifnet *ifp, struct rt * send the packet. */ if (ln->ln_state > ND6_LLINFO_INCOMPLETE) { + KERNEL_UNLOCK(); + sdl = satosdl(rt->rt_gateway); if (sdl->sdl_alen != ETHER_ADDR_LEN) { char addr[INET6_ADDRSTRLEN]; @@ -1332,7 +1336,6 @@ nd6_resolve(struct ifnet *ifp, struct rt } bcopy(LLADDR(sdl), desten, sdl->sdl_alen); - KERNEL_UNLOCK(); return (0); } @@ -1374,7 +1377,6 @@ nd6_resolve(struct ifnet *ifp, struct rt bad: m_freem(m); - KERNEL_UNLOCK(); return (EINVAL); }
usr.bin/mail: cmd3.c:bangexp(): "borked"?, and BSD fails POSIX compat
Hallo, and sorry for the cross-post, but so all in one (maybe) go. This is about a niche "feature" of mail, the shell command "bang" (! / ~! in compose mode): ? !echo no!bang no!bang ! ? set bang ? ! echo no!bang !echo nobang nobang ! ? ! ! !echo nobang nobang ! ? ! echo no!bang !echo noecho nobangbang noecho nobangbang ! ? In short: they all do it differently, and they all do it "wrong". - Apple mail simply does not act unless "bang" is set: bangexp() simply returns. - Free-, Net- and OpenBSD do not look for a "bang" variable at all, they simply do bang-style expansion whether that is set or not. - V10 mailx (and the code i maintain) do some mix (expand the "last bang" only if "bang" is set, but know about \! escapes and such. POSIX now says / will say If the bang variable is set, each unescaped occurrence of '!' in command shall be replaced with the command executed by the previous ! command or ˜! command escape. thus - All commands entered for ! and ~! shall be stored. - If "bang" is set, an unquoted ! shall be replaced by that storage. The code everywhere is more or less what Kurt A. Shoens wrote as commit ae3dba0da2068b0de91ef163ea95fe774196a501 Author: Kurt A. Schoens AuthorDate: 1980-10-09 02:48:47 -0800 Commit: Kurt A. Schoens CommitDate: 1980-10-09 02:48:47 -0800 Made shell escapes expand !'s to previous command SCCS-vsn: usr.bin/mail/cmd3.c 1.2 It will fail to properly "bang" a second time if some escaped question mark was present in the first round, while(*cp){ ... if (*cp == '!') { BANGEXP ... continue; } ... if (*cp == '\\' && cp[1] == '!') { ... *cp2++ = '!'; cp += 2; changed++; [fallthru] } ... *cp2++ = *cp++; and it stores and pushes through the second escape for "\!\!". I am thinking of dropping this entirely, as it can properly work only once if some escape took place, and i do not know how to fix this the right way. (And the line editor has history.) --steffen | |Der Kragenbaer,The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) |~~ |..and in spring, hear David Leonard sing.. | |The black bear, The black bear, |blithely holds his own holds himself at leisure |beating it, up and down tossing over his ups and downs with pleasure |~~ |Farewell, dear collar bear
rpki-client 8.4 released
rpki-client 8.4 has just been released and will be available in the rpki-client directory of any OpenBSD mirror soon. rpki-client is a FREE, easy-to-use implementation of the Resource Public Key Infrastructure (RPKI) for Relying Parties (RP) to facilitate validation of BGP announcements. The program queries the global RPKI repository system and validates untrusted network inputs. The program outputs validated ROA payloads, BGPsec Router keys, and ASPA payloads in configuration formats suitable for OpenBGPD and BIRD, and supports emitting CSV and JSON for consumption by other routing stacks. See RFC 6480 and RFC 6811 for a description of how RPKI and BGP Prefix Origin Validation help secure the global Internet routing system. rpki-client was primarily developed by Kristaps Dzonsons, Claudio Jeker, Job Snijders, Theo Buehler, Theo de Raadt and Sebastian Benoit as part of the OpenBSD Project. This release includes the following changes to the previous release: - The synchronisation protocol used to sync the repository is now included in the OpenMetrics output. - In filemode (-f option) the applicable manifests are now shown as part of the signature path. - A new -P option was added to manually specify a moment in time to use when parsing the validity window of certificates. Useful for regression testing. Default is invocation time of rpki-client. - The -A option will now also exclude ASPA data from the JSON output. - Improved accounting by tracking objects both by repo and tal. - For consistency, emit an explicit 'AS 0' Provider entry for ASPAs containing implicit information for one AFI. - Fix missing whitespace in ASPA-set output for OpenBGPD. (This bugfix was also released as OpenBSD 7.3 errata 001.) - Disallow X.509 v2 issuer and subject unique identifiers in certs. RPKI CAs will never issue certificates with V2 unique identifiers. - Check whether products listed on a manifest were issued by the same authority as the manifest itself. rpki-client works on all operating systems with a libcrypto library based on OpenSSL 1.1 or LibreSSL 3.5, and a libtls library compatible with LibreSSL 3.5 or later. rpki-client is known to compile and run on at least the following operating systems: Alpine, CentOS, Debian, Fedora, FreeBSD, Red Hat, Rocky, Ubuntu, macOS, and of course OpenBSD! It is our hope that packagers take interest and help adapt rpki-client-portable to more distributions. The mirrors where rpki-client can be found are on https://www.rpki-client.org/portable.html Reporting Bugs: === General bugs may be reported to tech@openbsd.org Portable bugs may be filed at https://github.com/rpki-client/rpki-client-portable We welcome feedback and improvements from the broader community. Thanks to all of the contributors who helped make this release possible. Assistance to coordinate security issues is available via secur...@openbsd.org.
nd6 mutex
Hi, I would like to introduce an neighbor discovery mutex like ARP uses it. Note that this does not unlock nd6 form kernel lock yet, this will be done in some follow up diffs. Lockless lookup in the hot path for ND6 will be harder than ARP as ln_state is always checked. Anyway let's get closer to that goal in small steps. ok? bluhm Index: netinet6/nd6.c === RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/nd6.c,v retrieving revision 1.273 diff -u -p -r1.273 nd6.c --- netinet6/nd6.c 2 May 2023 06:06:13 - 1.273 +++ netinet6/nd6.c 2 May 2023 14:41:14 - @@ -62,6 +62,15 @@ #include #include +/* + * Locks used to protect struct members in this file: + * a atomic operations + * I immutable after creation + * K kernel lock + * m nd6 mutex, needed when net lock is shared + * N net lock + */ + #define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */ #define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */ @@ -84,9 +93,13 @@ int nd6_debug = 1; int nd6_debug = 0; #endif -TAILQ_HEAD(llinfo_nd6_head, llinfo_nd6) nd6_list; -struct pool nd6_pool; /* pool for llinfo_nd6 structures */ -intnd6_inuse; +/* llinfo_nd6 live time, rt_llinfo and RTF_LLINFO are protected by nd6_mtx */ +struct mutex nd6_mtx = MUTEX_INITIALIZER(IPL_SOFTNET); + +TAILQ_HEAD(llinfo_nd6_head, llinfo_nd6) nd6_list = +TAILQ_HEAD_INITIALIZER(nd6_list); /* [mN] list of llinfo_nd6 structures */ +struct pool nd6_pool; /* [I] pool for llinfo_nd6 structures */ +intnd6_inuse; /* [m] limit neigbor discovery routes */ unsigned int ln_hold_total; /* [a] packets currently in the nd6 queue */ void nd6_timer(void *); @@ -105,7 +118,6 @@ struct task nd6_expire_task; void nd6_init(void) { - TAILQ_INIT(_list); pool_init(_pool, sizeof(struct llinfo_nd6), 0, IPL_SOFTNET, 0, "nd6", NULL); @@ -259,6 +271,7 @@ nd6_timer(void *unused) uptime = getuptime(); expire = uptime + nd6_gctimer; + /* Net lock is exclusive, no nd6 mutex needed for nd6_list here. */ TAILQ_FOREACH_SAFE(ln, _list, ln_list, nln) { struct rtentry *rt = ln->ln_rt; @@ -465,7 +478,7 @@ nd6_purge(struct ifnet *ifp) { struct llinfo_nd6 *ln, *nln; - NET_ASSERT_LOCKED(); + NET_ASSERT_LOCKED_EXCLUSIVE(); /* * Nuke neighbor cache entries for the ifp. @@ -627,13 +640,20 @@ nd6_is_addr_neighbor(const struct sockad void nd6_invalidate(struct rtentry *rt) { - struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; + struct llinfo_nd6 *ln; struct sockaddr_dl *sdl = satosdl(rt->rt_gateway); + mtx_enter(_mtx); + ln = (struct llinfo_nd6 *)rt->rt_llinfo; + if (ln == NULL) { + mtx_leave(_mtx); + return; + } atomic_sub_int(_hold_total, mq_purge(>ln_mq)); sdl->sdl_alen = 0; ln->ln_state = ND6_LLINFO_INCOMPLETE; ln->ln_asked = 0; + mtx_leave(_mtx); } /* @@ -804,20 +824,26 @@ nd6_rtrequest(struct ifnet *ifp, int req } satosdl(gate)->sdl_type = ifp->if_type; satosdl(gate)->sdl_index = ifp->if_index; - if (ln != NULL) - break; /* This happens on a route change */ /* * Case 2: This route may come from cloning, or a manual route * add with a LL address. */ ln = pool_get(_pool, PR_NOWAIT | PR_ZERO); - rt->rt_llinfo = (caddr_t)ln; if (ln == NULL) { log(LOG_DEBUG, "%s: pool get failed\n", __func__); break; } - mq_init(>ln_mq, LN_HOLD_QUEUE, IPL_SOFTNET); + + mtx_enter(_mtx); + if (rt->rt_llinfo != NULL) { + /* we lost the race, another thread has entered it */ + mtx_leave(_mtx); + pool_put(_pool, ln); + break; + } nd6_inuse++; + mq_init(>ln_mq, LN_HOLD_QUEUE, IPL_SOFTNET); + rt->rt_llinfo = (caddr_t)ln; ln->ln_rt = rt; /* this is required for "ndp" command. - shin */ if (req == RTM_ADD) { @@ -838,6 +864,7 @@ nd6_rtrequest(struct ifnet *ifp, int req } rt->rt_flags |= RTF_LLINFO; TAILQ_INSERT_HEAD(_list, ln, ln_list); + mtx_leave(_mtx); /* * If we have too many cache entries, initiate immediate @@ -850,6 +877,7 @@ nd6_rtrequest(struct ifnet *ifp, int req nd6_inuse >= ip6_neighborgcthresh) { int i; + mtx_enter(_mtx);
Re: rpki-client json.c add json_do_string()
On Tue, May 02, 2023 at 09:34:43AM -0600, Todd C. Miller wrote: > On Tue, 02 May 2023 14:13:27 +0200, Claudio Jeker wrote: > > > Add a json_do_string() a function to print a JSON string. > > This function does the needed encoding of control chars and escape chars. > > I skipped the optional encoding of the forward slash (/) since this is > > only needed if the json output is embedded in HTML/SGML/XML. > > People putting JSON into such documents need to pass this through an extra > > step. > > Unless you can guarantee that other control characters cannot occur > you probably want to output them in unicode hex form. For example, > something like: > > default: > const char hex[] = "0123456789abcdef"; > if (iscntrl(c)) { > /* Escape control characters like \u */ > if (!eb) > eb = fprintf(jsonfh, "\\u00%c%c", hex[c >> 4], > hex[c & 0x0f]) < 0; > } I think this is not really needed. All strings are previously checked for any bad characters. In general I think covering more than \n, \r and \t makes little sense (but I added the other ones just for completeness). Maybe the best is to check but error out for iscntrl(c). > In this case you probably want to assign c as an unsigned char. > E.g. > > while ((c = (unsigned char)*v++) != '\0') { > ... > } > > Or better yet just declare c as unsigned char instead of int. Good point. I guess it is better to use unsigned char here. -- :wq Claudio
Re: rpki-client json.c add json_do_string()
On Tue, 02 May 2023 14:13:27 +0200, Claudio Jeker wrote: > Add a json_do_string() a function to print a JSON string. > This function does the needed encoding of control chars and escape chars. > I skipped the optional encoding of the forward slash (/) since this is > only needed if the json output is embedded in HTML/SGML/XML. > People putting JSON into such documents need to pass this through an extra > step. Unless you can guarantee that other control characters cannot occur you probably want to output them in unicode hex form. For example, something like: default: const char hex[] = "0123456789abcdef"; if (iscntrl(c)) { /* Escape control characters like \u */ if (!eb) eb = fprintf(jsonfh, "\\u00%c%c", hex[c >> 4], hex[c & 0x0f]) < 0; } In this case you probably want to assign c as an unsigned char. E.g. while ((c = (unsigned char)*v++) != '\0') { ... } Or better yet just declare c as unsigned char instead of int. - todd
Re: rpki-client json.c add json_do_string()
On Tue, May 02, 2023 at 02:41:52PM +0200, Claudio Jeker wrote: > On Tue, May 02, 2023 at 02:29:20PM +0200, Theo Buehler wrote: > > On Tue, May 02, 2023 at 02:13:27PM +0200, Claudio Jeker wrote: > > > Add a json_do_string() a function to print a JSON string. > > > This function does the needed encoding of control chars and escape chars. > > > I skipped the optional encoding of the forward slash (/) since this is > > > only needed if the json output is embedded in HTML/SGML/XML. > > > People putting JSON into such documents need to pass this through an extra > > > step. > > > > > > This implements json_do_printf() now as a wrapper around json_do_string(). > > > All users of json_do_printf(name, "%s", value) can be converted to > > > json_do_string(). I will do this is a subsequent step. > > > > I'm ok with this. Some comments below. > > How about this version? By moving the !eb check into the while loop the > if checks fall away and so it is just one fprintf() per case. I think > trying to optimize this further is not that benefitial. I wonder if the > compiler wont do that better anyway. Agreed. ok
Re: rpki-client json.c add json_do_string()
On Tue, May 02, 2023 at 02:29:20PM +0200, Theo Buehler wrote: > On Tue, May 02, 2023 at 02:13:27PM +0200, Claudio Jeker wrote: > > Add a json_do_string() a function to print a JSON string. > > This function does the needed encoding of control chars and escape chars. > > I skipped the optional encoding of the forward slash (/) since this is > > only needed if the json output is embedded in HTML/SGML/XML. > > People putting JSON into such documents need to pass this through an extra > > step. > > > > This implements json_do_printf() now as a wrapper around json_do_string(). > > All users of json_do_printf(name, "%s", value) can be converted to > > json_do_string(). I will do this is a subsequent step. > > I'm ok with this. Some comments below. How about this version? By moving the !eb check into the while loop the if checks fall away and so it is just one fprintf() per case. I think trying to optimize this further is not that benefitial. I wonder if the compiler wont do that better anyway. -- :wq Claudio Index: json.c === RCS file: /cvs/src/usr.sbin/rpki-client/json.c,v retrieving revision 1.1 diff -u -p -r1.1 json.c --- json.c 27 Apr 2023 07:57:25 - 1.1 +++ json.c 2 May 2023 12:39:36 - @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "json.h" @@ -179,16 +180,56 @@ void json_do_printf(const char *name, const char *fmt, ...) { va_list ap; + char *str; - do_comma_indent(); + va_start(ap, fmt); + if (!eb) { + if (vasprintf(, fmt, ap) == -1) + errx(1, "json printf failed"); + json_do_string(name, str); + free(str); + } + va_end(ap); +} +void +json_do_string(const char *name, const char *v) +{ + int c; + + do_comma_indent(); do_name(name); if (!eb) eb = fprintf(jsonfh, "\"") < 0; - va_start(ap, fmt); - if (!eb) - eb = vfprintf(jsonfh, fmt, ap) < 0; - va_end(ap); + while ((c = *v++) != '\0' && !eb) { + /* skip escaping '/' since our use case does not require it */ + switch(c) { + case '"': + eb = fprintf(jsonfh, "\\\"") < 0; + break; + case '\\': + eb = fprintf(jsonfh, "") < 0; + break; + case '\b': + eb = fprintf(jsonfh, "\\b") < 0; + break; + case '\f': + eb = fprintf(jsonfh, "\\f") < 0; + break; + case '\n': + eb = fprintf(jsonfh, "\\n") < 0; + break; + case '\r': + eb = fprintf(jsonfh, "\\r") < 0; + break; + case '\t': + eb = fprintf(jsonfh, "\\t") < 0; + break; + default: + eb = putc(c, jsonfh) == EOF; + break; + } + } if (!eb) eb = fprintf(jsonfh, "\"") < 0; } Index: json.h === RCS file: /cvs/src/usr.sbin/rpki-client/json.h,v retrieving revision 1.1 diff -u -p -r1.1 json.h --- json.h 27 Apr 2023 07:57:25 - 1.1 +++ json.h 30 Apr 2023 15:11:36 - @@ -26,6 +26,7 @@ void json_do_object(const char *); void json_do_end(void); void json_do_printf(const char *, const char *, ...) __attribute__((__format__ (printf, 2, 3))); +void json_do_string(const char *, const char *); void json_do_hexdump(const char *, void *, size_t); void json_do_bool(const char *, int); void json_do_uint(const char *, unsigned long long);
Re: rpki-client json.c add json_do_string()
On Tue, May 02, 2023 at 02:13:27PM +0200, Claudio Jeker wrote: > Add a json_do_string() a function to print a JSON string. > This function does the needed encoding of control chars and escape chars. > I skipped the optional encoding of the forward slash (/) since this is > only needed if the json output is embedded in HTML/SGML/XML. > People putting JSON into such documents need to pass this through an extra > step. > > This implements json_do_printf() now as a wrapper around json_do_string(). > All users of json_do_printf(name, "%s", value) can be converted to > json_do_string(). I will do this is a subsequent step. I'm ok with this. Some comments below. > -- > :wq Claudio > > Index: json.c > === > RCS file: /cvs/src/usr.sbin/rpki-client/json.c,v > retrieving revision 1.1 > diff -u -p -r1.1 json.c > --- json.c27 Apr 2023 07:57:25 - 1.1 > +++ json.c1 May 2023 20:07:26 - > @@ -19,6 +19,7 @@ > #include > #include > #include > +#include I'd move this below stdio.h > #include > #include > > @@ -179,16 +180,64 @@ void > json_do_printf(const char *name, const char *fmt, ...) > { > va_list ap; > + char *str; > > - do_comma_indent(); > + va_start(ap, fmt); > + if (!eb) { > + if (vasprintf(, fmt, ap) == -1) > + errx(1, "json printf failed"); > + json_do_string(name, str); > + free(str); > + } > + va_end(ap); > +} > > +void > +json_do_string(const char *name, const char *v) > +{ > + int c; > + > + do_comma_indent(); > do_name(name); > if (!eb) > eb = fprintf(jsonfh, "\"") < 0; > - va_start(ap, fmt); > - if (!eb) > - eb = vfprintf(jsonfh, fmt, ap) < 0; > - va_end(ap); > + while ((c = *v++) != '\0') { Should this include && !eb? > + /* skip escaping '/' since our use case does not require it */ > + switch(c) { > + case '"': > + if (!eb) > + eb = fprintf(jsonfh, "\\\"") < 0; It's fine as it is, but the repetition could be avoided by assigning the escaped string or c to a temporary variable and do the printing after the switch. Your call. > + break; > + case '\\': > + if (!eb) > + eb = fprintf(jsonfh, "") < 0; > + break; > + case '\b': > + if (!eb) > + eb = fprintf(jsonfh, "\\b") < 0; > + break; > + case '\f': > + if (!eb) > + eb = fprintf(jsonfh, "\\f") < 0; > + break; > + case '\n': > + if (!eb) > + eb = fprintf(jsonfh, "\\n") < 0; > + break; > + case '\r': > + if (!eb) > + eb = fprintf(jsonfh, "\\r") < 0; > + break; > + case '\t': > + if (!eb) > + eb = fprintf(jsonfh, "\\t") < 0; > + break; > + default: > + if (!eb) > + eb = putc(c, jsonfh) == EOF; > + break; > + } > + } > if (!eb) > eb = fprintf(jsonfh, "\"") < 0; > } > Index: json.h > === > RCS file: /cvs/src/usr.sbin/rpki-client/json.h,v > retrieving revision 1.1 > diff -u -p -r1.1 json.h > --- json.h27 Apr 2023 07:57:25 - 1.1 > +++ json.h30 Apr 2023 15:11:36 - > @@ -26,6 +26,7 @@ voidjson_do_object(const char *); > void json_do_end(void); > void json_do_printf(const char *, const char *, ...) > __attribute__((__format__ (printf, 2, 3))); > +void json_do_string(const char *, const char *); > void json_do_hexdump(const char *, void *, size_t); > void json_do_bool(const char *, int); > void json_do_uint(const char *, unsigned long long); >
rpki-client json.c add json_do_string()
Add a json_do_string() a function to print a JSON string. This function does the needed encoding of control chars and escape chars. I skipped the optional encoding of the forward slash (/) since this is only needed if the json output is embedded in HTML/SGML/XML. People putting JSON into such documents need to pass this through an extra step. This implements json_do_printf() now as a wrapper around json_do_string(). All users of json_do_printf(name, "%s", value) can be converted to json_do_string(). I will do this is a subsequent step. -- :wq Claudio Index: json.c === RCS file: /cvs/src/usr.sbin/rpki-client/json.c,v retrieving revision 1.1 diff -u -p -r1.1 json.c --- json.c 27 Apr 2023 07:57:25 - 1.1 +++ json.c 1 May 2023 20:07:26 - @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -179,16 +180,64 @@ void json_do_printf(const char *name, const char *fmt, ...) { va_list ap; + char *str; - do_comma_indent(); + va_start(ap, fmt); + if (!eb) { + if (vasprintf(, fmt, ap) == -1) + errx(1, "json printf failed"); + json_do_string(name, str); + free(str); + } + va_end(ap); +} +void +json_do_string(const char *name, const char *v) +{ + int c; + + do_comma_indent(); do_name(name); if (!eb) eb = fprintf(jsonfh, "\"") < 0; - va_start(ap, fmt); - if (!eb) - eb = vfprintf(jsonfh, fmt, ap) < 0; - va_end(ap); + while ((c = *v++) != '\0') { + /* skip escaping '/' since our use case does not require it */ + switch(c) { + case '"': + if (!eb) + eb = fprintf(jsonfh, "\\\"") < 0; + break; + case '\\': + if (!eb) + eb = fprintf(jsonfh, "") < 0; + break; + case '\b': + if (!eb) + eb = fprintf(jsonfh, "\\b") < 0; + break; + case '\f': + if (!eb) + eb = fprintf(jsonfh, "\\f") < 0; + break; + case '\n': + if (!eb) + eb = fprintf(jsonfh, "\\n") < 0; + break; + case '\r': + if (!eb) + eb = fprintf(jsonfh, "\\r") < 0; + break; + case '\t': + if (!eb) + eb = fprintf(jsonfh, "\\t") < 0; + break; + default: + if (!eb) + eb = putc(c, jsonfh) == EOF; + break; + } + } if (!eb) eb = fprintf(jsonfh, "\"") < 0; } Index: json.h === RCS file: /cvs/src/usr.sbin/rpki-client/json.h,v retrieving revision 1.1 diff -u -p -r1.1 json.h --- json.h 27 Apr 2023 07:57:25 - 1.1 +++ json.h 30 Apr 2023 15:11:36 - @@ -26,6 +26,7 @@ void json_do_object(const char *); void json_do_end(void); void json_do_printf(const char *, const char *, ...) __attribute__((__format__ (printf, 2, 3))); +void json_do_string(const char *, const char *); void json_do_hexdump(const char *, void *, size_t); void json_do_bool(const char *, int); void json_do_uint(const char *, unsigned long long);