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$ 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 53302234Sbdrewery * or other actions occurred. 54231814Seadler */ 55231814Seadlerstatic int msgbuf_show_timestamp = 0; 56231814SeadlerSYSCTL_INT(_kern, OID_AUTO, msgbuf_show_timestamp, CTLFLAG_RW | CTLFLAG_TUN, 57231814Seadler &msgbuf_show_timestamp, 0, "Show timestamp in msgbuf"); 58231814SeadlerTUNABLE_INT("kern.msgbuf_show_timestamp", &msgbuf_show_timestamp); 59231814Seadler 60231814Seadler/* 61116660Siedowse * Initialize a message buffer of the specified size at the specified 62116660Siedowse * location. This also zeros the buffer area. 63116660Siedowse */ 64116660Siedowsevoid 65116660Siedowsemsgbuf_init(struct msgbuf *mbp, void *ptr, int size) 66116660Siedowse{ 67116660Siedowse 68116660Siedowse mbp->msg_ptr = ptr; 69116660Siedowse mbp->msg_size = size; 70116660Siedowse mbp->msg_seqmod = SEQMOD(size); 71116660Siedowse msgbuf_clear(mbp); 72116660Siedowse mbp->msg_magic = MSG_MAGIC; 73222537Sken mbp->msg_lastpri = -1; 74231814Seadler mbp->msg_flags = 0; 75222550Sken bzero(&mbp->msg_lock, sizeof(mbp->msg_lock)); 76222537Sken mtx_init(&mbp->msg_lock, "msgbuf", NULL, MTX_SPIN); 77116660Siedowse} 78116660Siedowse 79116660Siedowse/* 80116660Siedowse * Reinitialize a message buffer, retaining its previous contents if 81116660Siedowse * the size and checksum are correct. If the old contents cannot be 82116660Siedowse * recovered, the message buffer is cleared. 83116660Siedowse */ 84116660Siedowsevoid 85116660Siedowsemsgbuf_reinit(struct msgbuf *mbp, void *ptr, int size) 86116660Siedowse{ 87116660Siedowse u_int cksum; 88116660Siedowse 89116660Siedowse if (mbp->msg_magic != MSG_MAGIC || mbp->msg_size != size) { 90116660Siedowse msgbuf_init(mbp, ptr, size); 91116660Siedowse return; 92116660Siedowse } 93116660Siedowse mbp->msg_seqmod = SEQMOD(size); 94116660Siedowse mbp->msg_wseq = MSGBUF_SEQNORM(mbp, mbp->msg_wseq); 95116660Siedowse mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq); 96116660Siedowse mbp->msg_ptr = ptr; 97116660Siedowse cksum = msgbuf_cksum(mbp); 98116660Siedowse if (cksum != mbp->msg_cksum) { 99119765Sphk if (bootverbose) { 100119765Sphk printf("msgbuf cksum mismatch (read %x, calc %x)\n", 101119765Sphk mbp->msg_cksum, cksum); 102119765Sphk printf("Old msgbuf not recovered\n"); 103119765Sphk } 104116660Siedowse msgbuf_clear(mbp); 105116660Siedowse } 106222537Sken 107222537Sken mbp->msg_lastpri = -1; 108222537Sken /* Assume that the old message buffer didn't end in a newline. */ 109231814Seadler mbp->msg_flags |= MSGBUF_NEEDNL; 110222550Sken bzero(&mbp->msg_lock, sizeof(mbp->msg_lock)); 111222537Sken mtx_init(&mbp->msg_lock, "msgbuf", NULL, MTX_SPIN); 112116660Siedowse} 113116660Siedowse 114116660Siedowse/* 115116660Siedowse * Clear the message buffer. 116116660Siedowse */ 117116660Siedowsevoid 118116660Siedowsemsgbuf_clear(struct msgbuf *mbp) 119116660Siedowse{ 120116660Siedowse 121116660Siedowse bzero(mbp->msg_ptr, mbp->msg_size); 122116660Siedowse mbp->msg_wseq = 0; 123116660Siedowse mbp->msg_rseq = 0; 124116660Siedowse mbp->msg_cksum = 0; 125116660Siedowse} 126116660Siedowse 127116660Siedowse/* 128116660Siedowse * Get a count of the number of unread characters in the message buffer. 129116660Siedowse */ 130116660Siedowseint 131116660Siedowsemsgbuf_getcount(struct msgbuf *mbp) 132116660Siedowse{ 133116660Siedowse u_int len; 134116660Siedowse 135116660Siedowse len = MSGBUF_SEQSUB(mbp, mbp->msg_wseq, mbp->msg_rseq); 136116660Siedowse if (len > mbp->msg_size) 137116660Siedowse len = mbp->msg_size; 138116660Siedowse return (len); 139116660Siedowse} 140116660Siedowse 141116660Siedowse/* 142222537Sken * Add a character into the message buffer, and update the checksum and 143222537Sken * sequence number. 144222537Sken * 145222537Sken * The caller should hold the message buffer spinlock. 146116660Siedowse */ 147233135Seadler 148233135Seadlerstatic void 149233135Seadlermsgbuf_do_addchar(struct msgbuf * const mbp, u_int * const seq, const int c) 150222537Sken{ 151222537Sken u_int pos; 152222537Sken 153222537Sken /* Make sure we properly wrap the sequence number. */ 154222537Sken pos = MSGBUF_SEQ_TO_POS(mbp, *seq); 155233135Seadler mbp->msg_cksum += (u_int)(u_char)c - 156222537Sken (u_int)(u_char)mbp->msg_ptr[pos]; 157222537Sken mbp->msg_ptr[pos] = c; 158222537Sken *seq = MSGBUF_SEQNORM(mbp, *seq + 1); 159222537Sken} 160222537Sken 161222537Sken/* 162222537Sken * Append a character to a message buffer. 163222537Sken */ 164116660Siedowsevoid 165116660Siedowsemsgbuf_addchar(struct msgbuf *mbp, int c) 166116660Siedowse{ 167222537Sken mtx_lock_spin(&mbp->msg_lock); 168116660Siedowse 169222537Sken msgbuf_do_addchar(mbp, &mbp->msg_wseq, c); 170222537Sken 171222537Sken mtx_unlock_spin(&mbp->msg_lock); 172116660Siedowse} 173116660Siedowse 174116660Siedowse/* 175222537Sken * Append a NUL-terminated string with a priority to a message buffer. 176222537Sken * Filter carriage returns if the caller requests it. 177222537Sken * 178222537Sken * XXX The carriage return filtering behavior is present in the 179222537Sken * msglogchar() API, however testing has shown that we don't seem to send 180222537Sken * carriage returns down this path. So do we still need it? 181222537Sken */ 182222537Skenvoid 183222537Skenmsgbuf_addstr(struct msgbuf *mbp, int pri, char *str, int filter_cr) 184222537Sken{ 185222537Sken u_int seq; 186222537Sken size_t len, prefix_len; 187222537Sken char prefix[MAXPRIBUF]; 188233135Seadler char buf[32]; 189233135Seadler int nl, i, j, needtime; 190222537Sken 191222537Sken len = strlen(str); 192222537Sken prefix_len = 0; 193222537Sken nl = 0; 194222537Sken 195222537Sken /* If we have a zero-length string, no need to do anything. */ 196222537Sken if (len == 0) 197222537Sken return; 198222537Sken 199222537Sken mtx_lock_spin(&mbp->msg_lock); 200222537Sken 201222537Sken /* 202222537Sken * If this is true, we may need to insert a new priority sequence, 203222537Sken * so prepare the prefix. 204222537Sken */ 205222537Sken if (pri != -1) 206222537Sken prefix_len = sprintf(prefix, "<%d>", pri); 207222537Sken 208222537Sken /* 209222537Sken * Starting write sequence number. 210222537Sken */ 211222537Sken seq = mbp->msg_wseq; 212222537Sken 213222537Sken /* 214222537Sken * Whenever there is a change in priority, we have to insert a 215222537Sken * newline, and a priority prefix if the priority is not -1. Here 216222537Sken * we detect whether there was a priority change, and whether we 217222537Sken * did not end with a newline. If that is the case, we need to 218222537Sken * insert a newline before this string. 219222537Sken */ 220231814Seadler if (mbp->msg_lastpri != pri && (mbp->msg_flags & MSGBUF_NEEDNL) != 0) { 221222537Sken 222222537Sken msgbuf_do_addchar(mbp, &seq, '\n'); 223231814Seadler mbp->msg_flags &= ~MSGBUF_NEEDNL; 224222537Sken } 225222537Sken 226233135Seadler needtime = 1; 227222537Sken for (i = 0; i < len; i++) { 228222537Sken /* 229222537Sken * If we just had a newline, and the priority is not -1 230222537Sken * (and therefore prefix_len != 0), then we need a priority 231222537Sken * prefix for this line. 232222537Sken */ 233231814Seadler if ((mbp->msg_flags & MSGBUF_NEEDNL) == 0 && prefix_len != 0) { 234222537Sken int j; 235222537Sken 236222537Sken for (j = 0; j < prefix_len; j++) 237222537Sken msgbuf_do_addchar(mbp, &seq, prefix[j]); 238222537Sken } 239222537Sken 240233135Seadler if (msgbuf_show_timestamp && needtime == 1 && 241233135Seadler (mbp->msg_flags & MSGBUF_NEEDNL) == 0) { 242233135Seadler 243233135Seadler snprintf(buf, sizeof(buf), "[%jd] ", 244233135Seadler (intmax_t)time_uptime); 245233135Seadler for (j = 0; buf[j] != '\0'; j++) 246233135Seadler msgbuf_do_addchar(mbp, &seq, buf[j]); 247233135Seadler needtime = 0; 248233135Seadler } 249233135Seadler 250222537Sken /* 251222537Sken * Don't copy carriage returns if the caller requested 252222537Sken * filtering. 253222537Sken * 254222537Sken * XXX This matches the behavior of msglogchar(), but is it 255222537Sken * necessary? Testing has shown that we don't seem to get 256222537Sken * carriage returns here. 257222537Sken */ 258222537Sken if ((filter_cr != 0) && (str[i] == '\r')) 259222537Sken continue; 260222537Sken 261222537Sken /* 262222537Sken * Clear this flag if we see a newline. This affects whether 263222537Sken * we need to insert a new prefix or insert a newline later. 264222537Sken */ 265222537Sken if (str[i] == '\n') 266231814Seadler mbp->msg_flags &= ~MSGBUF_NEEDNL; 267222537Sken else 268231814Seadler mbp->msg_flags |= MSGBUF_NEEDNL; 269222537Sken 270222537Sken msgbuf_do_addchar(mbp, &seq, str[i]); 271222537Sken } 272222537Sken /* 273222537Sken * Update the write sequence number for the actual number of 274222537Sken * characters we put in the message buffer. (Depends on whether 275222537Sken * carriage returns are filtered.) 276222537Sken */ 277222537Sken mbp->msg_wseq = seq; 278222537Sken 279222537Sken /* 280222537Sken * Set the last priority. 281222537Sken */ 282222537Sken mbp->msg_lastpri = pri; 283222537Sken 284222537Sken mtx_unlock_spin(&mbp->msg_lock); 285222537Sken 286222537Sken} 287222537Sken 288222537Sken/* 289116660Siedowse * Read and mark as read a character from a message buffer. 290116660Siedowse * Returns the character, or -1 if no characters are available. 291116660Siedowse */ 292116660Siedowseint 293116660Siedowsemsgbuf_getchar(struct msgbuf *mbp) 294116660Siedowse{ 295116660Siedowse u_int len, wseq; 296116660Siedowse int c; 297116660Siedowse 298222537Sken mtx_lock_spin(&mbp->msg_lock); 299222537Sken 300116660Siedowse wseq = mbp->msg_wseq; 301116660Siedowse len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq); 302222537Sken if (len == 0) { 303222537Sken mtx_unlock_spin(&mbp->msg_lock); 304116660Siedowse return (-1); 305222537Sken } 306116660Siedowse if (len > mbp->msg_size) 307116660Siedowse mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size); 308116660Siedowse c = (u_char)mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq)]; 309116660Siedowse mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + 1); 310222537Sken 311222537Sken mtx_unlock_spin(&mbp->msg_lock); 312222537Sken 313116660Siedowse return (c); 314116660Siedowse} 315116660Siedowse 316116660Siedowse/* 317116660Siedowse * Read and mark as read a number of characters from a message buffer. 318116660Siedowse * Returns the number of characters that were placed in `buf'. 319116660Siedowse */ 320116660Siedowseint 321116660Siedowsemsgbuf_getbytes(struct msgbuf *mbp, char *buf, int buflen) 322116660Siedowse{ 323116660Siedowse u_int len, pos, wseq; 324116660Siedowse 325222537Sken mtx_lock_spin(&mbp->msg_lock); 326222537Sken 327116660Siedowse wseq = mbp->msg_wseq; 328116660Siedowse len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq); 329222537Sken if (len == 0) { 330222537Sken mtx_unlock_spin(&mbp->msg_lock); 331116660Siedowse return (0); 332222537Sken } 333116660Siedowse if (len > mbp->msg_size) { 334116660Siedowse mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size); 335116660Siedowse len = mbp->msg_size; 336116660Siedowse } 337116660Siedowse pos = MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq); 338116660Siedowse len = min(len, mbp->msg_size - pos); 339116660Siedowse len = min(len, (u_int)buflen); 340116660Siedowse 341116660Siedowse bcopy(&mbp->msg_ptr[pos], buf, len); 342116660Siedowse mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + len); 343222537Sken 344222537Sken mtx_unlock_spin(&mbp->msg_lock); 345222537Sken 346116660Siedowse return (len); 347116660Siedowse} 348116660Siedowse 349116660Siedowse/* 350116660Siedowse * Peek at the full contents of a message buffer without marking any 351116660Siedowse * data as read. `seqp' should point to an unsigned integer that 352116660Siedowse * msgbuf_peekbytes() can use to retain state between calls so that 353116660Siedowse * the whole message buffer can be read in multiple short reads. 354116660Siedowse * To initialise this variable to the start of the message buffer, 355116660Siedowse * call msgbuf_peekbytes() with a NULL `buf' parameter. 356116660Siedowse * 357116660Siedowse * Returns the number of characters that were placed in `buf'. 358116660Siedowse */ 359116660Siedowseint 360116660Siedowsemsgbuf_peekbytes(struct msgbuf *mbp, char *buf, int buflen, u_int *seqp) 361116660Siedowse{ 362116660Siedowse u_int len, pos, wseq; 363116660Siedowse 364222537Sken mtx_lock_spin(&mbp->msg_lock); 365222537Sken 366116660Siedowse if (buf == NULL) { 367116660Siedowse /* Just initialise *seqp. */ 368116660Siedowse *seqp = MSGBUF_SEQNORM(mbp, mbp->msg_wseq - mbp->msg_size); 369222537Sken mtx_unlock_spin(&mbp->msg_lock); 370116660Siedowse return (0); 371116660Siedowse } 372116660Siedowse 373116660Siedowse wseq = mbp->msg_wseq; 374116660Siedowse len = MSGBUF_SEQSUB(mbp, wseq, *seqp); 375222537Sken if (len == 0) { 376222537Sken mtx_unlock_spin(&mbp->msg_lock); 377116660Siedowse return (0); 378222537Sken } 379116660Siedowse if (len > mbp->msg_size) { 380116660Siedowse *seqp = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size); 381116660Siedowse len = mbp->msg_size; 382116660Siedowse } 383116660Siedowse pos = MSGBUF_SEQ_TO_POS(mbp, *seqp); 384116660Siedowse len = min(len, mbp->msg_size - pos); 385116660Siedowse len = min(len, (u_int)buflen); 386116660Siedowse bcopy(&mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, *seqp)], buf, len); 387116660Siedowse *seqp = MSGBUF_SEQNORM(mbp, *seqp + len); 388222537Sken 389222537Sken mtx_unlock_spin(&mbp->msg_lock); 390222537Sken 391116660Siedowse return (len); 392116660Siedowse} 393116660Siedowse 394116660Siedowse/* 395116660Siedowse * Compute the checksum for the complete message buffer contents. 396116660Siedowse */ 397116660Siedowsestatic u_int 398116660Siedowsemsgbuf_cksum(struct msgbuf *mbp) 399116660Siedowse{ 400116660Siedowse u_int i, sum; 401116660Siedowse 402116660Siedowse sum = 0; 403116660Siedowse for (i = 0; i < mbp->msg_size; i++) 404116660Siedowse sum += (u_char)mbp->msg_ptr[i]; 405116660Siedowse return (sum); 406116660Siedowse} 407116660Siedowse 408116660Siedowse/* 409116660Siedowse * Copy from one message buffer to another. 410116660Siedowse */ 411116660Siedowsevoid 412116660Siedowsemsgbuf_copy(struct msgbuf *src, struct msgbuf *dst) 413116660Siedowse{ 414116660Siedowse int c; 415116660Siedowse 416116660Siedowse while ((c = msgbuf_getchar(src)) >= 0) 417116660Siedowse msgbuf_addchar(dst, c); 418116660Siedowse} 419