1139804Simp/*- 2116660Siedowse * Copyright (c) 2003 Ian Dowse. All rights reserved. 3116660Siedowse * 4116660Siedowse * Redistribution and use in source and binary forms, with or without 5116660Siedowse * modification, are permitted provided that the following conditions 6116660Siedowse * are met: 7116660Siedowse * 1. Redistributions of source code must retain the above copyright 8116660Siedowse * notice, this list of conditions and the following disclaimer. 9116660Siedowse * 2. Redistributions in binary form must reproduce the above copyright 10116660Siedowse * notice, this list of conditions and the following disclaimer in the 11116660Siedowse * documentation and/or other materials provided with the distribution. 12116660Siedowse * 13116660Siedowse * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14116660Siedowse * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15116660Siedowse * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16116660Siedowse * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17116660Siedowse * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18116660Siedowse * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19116660Siedowse * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20116660Siedowse * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21116660Siedowse * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22116660Siedowse * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23116660Siedowse * SUCH DAMAGE. 24116660Siedowse * 25116660Siedowse * $FreeBSD: stable/11/sys/kern/subr_msgbuf.c 338109 2018-08-20 17:27:30Z kevans $ 26116660Siedowse */ 27116660Siedowse 28116660Siedowse/* 29116660Siedowse * Generic message buffer support routines. 30116660Siedowse */ 31116660Siedowse 32116660Siedowse#include <sys/param.h> 33116660Siedowse#include <sys/systm.h> 34222537Sken#include <sys/lock.h> 35231814Seadler#include <sys/kernel.h> 36222537Sken#include <sys/mutex.h> 37116660Siedowse#include <sys/msgbuf.h> 38231814Seadler#include <sys/sysctl.h> 39116660Siedowse 40222537Sken/* 41222537Sken * Maximum number conversion buffer length: uintmax_t in base 2, plus <> 42222537Sken * around the priority, and a terminating NUL. 43222537Sken */ 44222537Sken#define MAXPRIBUF (sizeof(intmax_t) * NBBY + 3) 45222537Sken 46116660Siedowse/* Read/write sequence numbers are modulo a multiple of the buffer size. */ 47116660Siedowse#define SEQMOD(size) ((size) * 16) 48116660Siedowse 49116660Siedowsestatic u_int msgbuf_cksum(struct msgbuf *mbp); 50116660Siedowse 51116660Siedowse/* 52233135Seadler * Timestamps in msgbuf are useful when trying to diagnose when core dumps 53298819Spfg * or other actions occurred. 54231814Seadler */ 55231814Seadlerstatic int msgbuf_show_timestamp = 0; 56267992ShselaskySYSCTL_INT(_kern, OID_AUTO, msgbuf_show_timestamp, CTLFLAG_RWTUN, 57231814Seadler &msgbuf_show_timestamp, 0, "Show timestamp in msgbuf"); 58231814Seadler 59231814Seadler/* 60116660Siedowse * Initialize a message buffer of the specified size at the specified 61116660Siedowse * location. This also zeros the buffer area. 62116660Siedowse */ 63116660Siedowsevoid 64116660Siedowsemsgbuf_init(struct msgbuf *mbp, void *ptr, int size) 65116660Siedowse{ 66116660Siedowse 67116660Siedowse mbp->msg_ptr = ptr; 68116660Siedowse mbp->msg_size = size; 69116660Siedowse mbp->msg_seqmod = SEQMOD(size); 70116660Siedowse msgbuf_clear(mbp); 71116660Siedowse mbp->msg_magic = MSG_MAGIC; 72222537Sken mbp->msg_lastpri = -1; 73231814Seadler mbp->msg_flags = 0; 74222550Sken bzero(&mbp->msg_lock, sizeof(mbp->msg_lock)); 75222537Sken mtx_init(&mbp->msg_lock, "msgbuf", NULL, MTX_SPIN); 76116660Siedowse} 77116660Siedowse 78116660Siedowse/* 79116660Siedowse * Reinitialize a message buffer, retaining its previous contents if 80116660Siedowse * the size and checksum are correct. If the old contents cannot be 81116660Siedowse * recovered, the message buffer is cleared. 82116660Siedowse */ 83116660Siedowsevoid 84116660Siedowsemsgbuf_reinit(struct msgbuf *mbp, void *ptr, int size) 85116660Siedowse{ 86116660Siedowse u_int cksum; 87116660Siedowse 88116660Siedowse if (mbp->msg_magic != MSG_MAGIC || mbp->msg_size != size) { 89116660Siedowse msgbuf_init(mbp, ptr, size); 90116660Siedowse return; 91116660Siedowse } 92116660Siedowse mbp->msg_seqmod = SEQMOD(size); 93116660Siedowse mbp->msg_wseq = MSGBUF_SEQNORM(mbp, mbp->msg_wseq); 94116660Siedowse mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq); 95116660Siedowse mbp->msg_ptr = ptr; 96116660Siedowse cksum = msgbuf_cksum(mbp); 97116660Siedowse if (cksum != mbp->msg_cksum) { 98119765Sphk if (bootverbose) { 99119765Sphk printf("msgbuf cksum mismatch (read %x, calc %x)\n", 100119765Sphk mbp->msg_cksum, cksum); 101119765Sphk printf("Old msgbuf not recovered\n"); 102119765Sphk } 103116660Siedowse msgbuf_clear(mbp); 104116660Siedowse } 105222537Sken 106222537Sken mbp->msg_lastpri = -1; 107222537Sken /* Assume that the old message buffer didn't end in a newline. */ 108231814Seadler mbp->msg_flags |= MSGBUF_NEEDNL; 109222550Sken bzero(&mbp->msg_lock, sizeof(mbp->msg_lock)); 110222537Sken mtx_init(&mbp->msg_lock, "msgbuf", NULL, MTX_SPIN); 111116660Siedowse} 112116660Siedowse 113116660Siedowse/* 114116660Siedowse * Clear the message buffer. 115116660Siedowse */ 116116660Siedowsevoid 117116660Siedowsemsgbuf_clear(struct msgbuf *mbp) 118116660Siedowse{ 119116660Siedowse 120116660Siedowse bzero(mbp->msg_ptr, mbp->msg_size); 121116660Siedowse mbp->msg_wseq = 0; 122116660Siedowse mbp->msg_rseq = 0; 123116660Siedowse mbp->msg_cksum = 0; 124116660Siedowse} 125116660Siedowse 126116660Siedowse/* 127116660Siedowse * Get a count of the number of unread characters in the message buffer. 128116660Siedowse */ 129116660Siedowseint 130116660Siedowsemsgbuf_getcount(struct msgbuf *mbp) 131116660Siedowse{ 132116660Siedowse u_int len; 133116660Siedowse 134116660Siedowse len = MSGBUF_SEQSUB(mbp, mbp->msg_wseq, mbp->msg_rseq); 135116660Siedowse if (len > mbp->msg_size) 136116660Siedowse len = mbp->msg_size; 137116660Siedowse return (len); 138116660Siedowse} 139116660Siedowse 140116660Siedowse/* 141222537Sken * Add a character into the message buffer, and update the checksum and 142222537Sken * sequence number. 143222537Sken * 144222537Sken * The caller should hold the message buffer spinlock. 145116660Siedowse */ 146233135Seadler 147233135Seadlerstatic void 148233135Seadlermsgbuf_do_addchar(struct msgbuf * const mbp, u_int * const seq, const int c) 149222537Sken{ 150222537Sken u_int pos; 151222537Sken 152222537Sken /* Make sure we properly wrap the sequence number. */ 153222537Sken pos = MSGBUF_SEQ_TO_POS(mbp, *seq); 154233135Seadler mbp->msg_cksum += (u_int)(u_char)c - 155222537Sken (u_int)(u_char)mbp->msg_ptr[pos]; 156222537Sken mbp->msg_ptr[pos] = c; 157222537Sken *seq = MSGBUF_SEQNORM(mbp, *seq + 1); 158222537Sken} 159222537Sken 160222537Sken/* 161222537Sken * Append a character to a message buffer. 162222537Sken */ 163116660Siedowsevoid 164116660Siedowsemsgbuf_addchar(struct msgbuf *mbp, int c) 165116660Siedowse{ 166222537Sken mtx_lock_spin(&mbp->msg_lock); 167116660Siedowse 168222537Sken msgbuf_do_addchar(mbp, &mbp->msg_wseq, c); 169222537Sken 170222537Sken mtx_unlock_spin(&mbp->msg_lock); 171116660Siedowse} 172116660Siedowse 173116660Siedowse/* 174222537Sken * Append a NUL-terminated string with a priority to a message buffer. 175222537Sken * Filter carriage returns if the caller requests it. 176222537Sken * 177222537Sken * XXX The carriage return filtering behavior is present in the 178222537Sken * msglogchar() API, however testing has shown that we don't seem to send 179222537Sken * carriage returns down this path. So do we still need it? 180222537Sken */ 181222537Skenvoid 182338109Skevansmsgbuf_addstr(struct msgbuf *mbp, int pri, const char *str, int filter_cr) 183222537Sken{ 184222537Sken u_int seq; 185222537Sken size_t len, prefix_len; 186222537Sken char prefix[MAXPRIBUF]; 187233135Seadler char buf[32]; 188233135Seadler int nl, i, j, needtime; 189222537Sken 190222537Sken len = strlen(str); 191222537Sken prefix_len = 0; 192222537Sken nl = 0; 193222537Sken 194222537Sken /* If we have a zero-length string, no need to do anything. */ 195222537Sken if (len == 0) 196222537Sken return; 197222537Sken 198222537Sken mtx_lock_spin(&mbp->msg_lock); 199222537Sken 200222537Sken /* 201222537Sken * If this is true, we may need to insert a new priority sequence, 202222537Sken * so prepare the prefix. 203222537Sken */ 204222537Sken if (pri != -1) 205222537Sken prefix_len = sprintf(prefix, "<%d>", pri); 206222537Sken 207222537Sken /* 208222537Sken * Starting write sequence number. 209222537Sken */ 210222537Sken seq = mbp->msg_wseq; 211222537Sken 212222537Sken /* 213222537Sken * Whenever there is a change in priority, we have to insert a 214222537Sken * newline, and a priority prefix if the priority is not -1. Here 215222537Sken * we detect whether there was a priority change, and whether we 216222537Sken * did not end with a newline. If that is the case, we need to 217222537Sken * insert a newline before this string. 218222537Sken */ 219231814Seadler if (mbp->msg_lastpri != pri && (mbp->msg_flags & MSGBUF_NEEDNL) != 0) { 220222537Sken 221222537Sken msgbuf_do_addchar(mbp, &seq, '\n'); 222231814Seadler mbp->msg_flags &= ~MSGBUF_NEEDNL; 223222537Sken } 224222537Sken 225233135Seadler needtime = 1; 226222537Sken for (i = 0; i < len; i++) { 227222537Sken /* 228222537Sken * If we just had a newline, and the priority is not -1 229222537Sken * (and therefore prefix_len != 0), then we need a priority 230222537Sken * prefix for this line. 231222537Sken */ 232231814Seadler if ((mbp->msg_flags & MSGBUF_NEEDNL) == 0 && prefix_len != 0) { 233222537Sken int j; 234222537Sken 235222537Sken for (j = 0; j < prefix_len; j++) 236222537Sken msgbuf_do_addchar(mbp, &seq, prefix[j]); 237222537Sken } 238222537Sken 239233135Seadler if (msgbuf_show_timestamp && needtime == 1 && 240233135Seadler (mbp->msg_flags & MSGBUF_NEEDNL) == 0) { 241233135Seadler 242233135Seadler snprintf(buf, sizeof(buf), "[%jd] ", 243233135Seadler (intmax_t)time_uptime); 244233135Seadler for (j = 0; buf[j] != '\0'; j++) 245233135Seadler msgbuf_do_addchar(mbp, &seq, buf[j]); 246233135Seadler needtime = 0; 247233135Seadler } 248233135Seadler 249222537Sken /* 250222537Sken * Don't copy carriage returns if the caller requested 251222537Sken * filtering. 252222537Sken * 253222537Sken * XXX This matches the behavior of msglogchar(), but is it 254222537Sken * necessary? Testing has shown that we don't seem to get 255222537Sken * carriage returns here. 256222537Sken */ 257222537Sken if ((filter_cr != 0) && (str[i] == '\r')) 258222537Sken continue; 259222537Sken 260222537Sken /* 261222537Sken * Clear this flag if we see a newline. This affects whether 262222537Sken * we need to insert a new prefix or insert a newline later. 263222537Sken */ 264222537Sken if (str[i] == '\n') 265231814Seadler mbp->msg_flags &= ~MSGBUF_NEEDNL; 266222537Sken else 267231814Seadler mbp->msg_flags |= MSGBUF_NEEDNL; 268222537Sken 269222537Sken msgbuf_do_addchar(mbp, &seq, str[i]); 270222537Sken } 271222537Sken /* 272222537Sken * Update the write sequence number for the actual number of 273222537Sken * characters we put in the message buffer. (Depends on whether 274222537Sken * carriage returns are filtered.) 275222537Sken */ 276222537Sken mbp->msg_wseq = seq; 277222537Sken 278222537Sken /* 279222537Sken * Set the last priority. 280222537Sken */ 281222537Sken mbp->msg_lastpri = pri; 282222537Sken 283222537Sken mtx_unlock_spin(&mbp->msg_lock); 284222537Sken 285222537Sken} 286222537Sken 287222537Sken/* 288116660Siedowse * Read and mark as read a character from a message buffer. 289116660Siedowse * Returns the character, or -1 if no characters are available. 290116660Siedowse */ 291116660Siedowseint 292116660Siedowsemsgbuf_getchar(struct msgbuf *mbp) 293116660Siedowse{ 294116660Siedowse u_int len, wseq; 295116660Siedowse int c; 296116660Siedowse 297222537Sken mtx_lock_spin(&mbp->msg_lock); 298222537Sken 299116660Siedowse wseq = mbp->msg_wseq; 300116660Siedowse len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq); 301222537Sken if (len == 0) { 302222537Sken mtx_unlock_spin(&mbp->msg_lock); 303116660Siedowse return (-1); 304222537Sken } 305116660Siedowse if (len > mbp->msg_size) 306116660Siedowse mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size); 307116660Siedowse c = (u_char)mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq)]; 308116660Siedowse mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + 1); 309222537Sken 310222537Sken mtx_unlock_spin(&mbp->msg_lock); 311222537Sken 312116660Siedowse return (c); 313116660Siedowse} 314116660Siedowse 315116660Siedowse/* 316116660Siedowse * Read and mark as read a number of characters from a message buffer. 317116660Siedowse * Returns the number of characters that were placed in `buf'. 318116660Siedowse */ 319116660Siedowseint 320116660Siedowsemsgbuf_getbytes(struct msgbuf *mbp, char *buf, int buflen) 321116660Siedowse{ 322116660Siedowse u_int len, pos, wseq; 323116660Siedowse 324222537Sken mtx_lock_spin(&mbp->msg_lock); 325222537Sken 326116660Siedowse wseq = mbp->msg_wseq; 327116660Siedowse len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq); 328222537Sken if (len == 0) { 329222537Sken mtx_unlock_spin(&mbp->msg_lock); 330116660Siedowse return (0); 331222537Sken } 332116660Siedowse if (len > mbp->msg_size) { 333116660Siedowse mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size); 334116660Siedowse len = mbp->msg_size; 335116660Siedowse } 336116660Siedowse pos = MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq); 337116660Siedowse len = min(len, mbp->msg_size - pos); 338116660Siedowse len = min(len, (u_int)buflen); 339116660Siedowse 340116660Siedowse bcopy(&mbp->msg_ptr[pos], buf, len); 341116660Siedowse mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + len); 342222537Sken 343222537Sken mtx_unlock_spin(&mbp->msg_lock); 344222537Sken 345116660Siedowse return (len); 346116660Siedowse} 347116660Siedowse 348116660Siedowse/* 349116660Siedowse * Peek at the full contents of a message buffer without marking any 350116660Siedowse * data as read. `seqp' should point to an unsigned integer that 351116660Siedowse * msgbuf_peekbytes() can use to retain state between calls so that 352116660Siedowse * the whole message buffer can be read in multiple short reads. 353116660Siedowse * To initialise this variable to the start of the message buffer, 354116660Siedowse * call msgbuf_peekbytes() with a NULL `buf' parameter. 355116660Siedowse * 356116660Siedowse * Returns the number of characters that were placed in `buf'. 357116660Siedowse */ 358116660Siedowseint 359116660Siedowsemsgbuf_peekbytes(struct msgbuf *mbp, char *buf, int buflen, u_int *seqp) 360116660Siedowse{ 361116660Siedowse u_int len, pos, wseq; 362116660Siedowse 363222537Sken mtx_lock_spin(&mbp->msg_lock); 364222537Sken 365116660Siedowse if (buf == NULL) { 366116660Siedowse /* Just initialise *seqp. */ 367116660Siedowse *seqp = MSGBUF_SEQNORM(mbp, mbp->msg_wseq - mbp->msg_size); 368222537Sken mtx_unlock_spin(&mbp->msg_lock); 369116660Siedowse return (0); 370116660Siedowse } 371116660Siedowse 372116660Siedowse wseq = mbp->msg_wseq; 373116660Siedowse len = MSGBUF_SEQSUB(mbp, wseq, *seqp); 374222537Sken if (len == 0) { 375222537Sken mtx_unlock_spin(&mbp->msg_lock); 376116660Siedowse return (0); 377222537Sken } 378116660Siedowse if (len > mbp->msg_size) { 379116660Siedowse *seqp = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size); 380116660Siedowse len = mbp->msg_size; 381116660Siedowse } 382116660Siedowse pos = MSGBUF_SEQ_TO_POS(mbp, *seqp); 383116660Siedowse len = min(len, mbp->msg_size - pos); 384116660Siedowse len = min(len, (u_int)buflen); 385116660Siedowse bcopy(&mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, *seqp)], buf, len); 386116660Siedowse *seqp = MSGBUF_SEQNORM(mbp, *seqp + len); 387222537Sken 388222537Sken mtx_unlock_spin(&mbp->msg_lock); 389222537Sken 390116660Siedowse return (len); 391116660Siedowse} 392116660Siedowse 393116660Siedowse/* 394116660Siedowse * Compute the checksum for the complete message buffer contents. 395116660Siedowse */ 396116660Siedowsestatic u_int 397116660Siedowsemsgbuf_cksum(struct msgbuf *mbp) 398116660Siedowse{ 399116660Siedowse u_int i, sum; 400116660Siedowse 401116660Siedowse sum = 0; 402116660Siedowse for (i = 0; i < mbp->msg_size; i++) 403116660Siedowse sum += (u_char)mbp->msg_ptr[i]; 404116660Siedowse return (sum); 405116660Siedowse} 406116660Siedowse 407116660Siedowse/* 408116660Siedowse * Copy from one message buffer to another. 409116660Siedowse */ 410116660Siedowsevoid 411116660Siedowsemsgbuf_copy(struct msgbuf *src, struct msgbuf *dst) 412116660Siedowse{ 413116660Siedowse int c; 414116660Siedowse 415116660Siedowse while ((c = msgbuf_getchar(src)) >= 0) 416116660Siedowse msgbuf_addchar(dst, c); 417116660Siedowse} 418