Friday August 19, 2005
In my
earlier entry I discussed the issues with flow control. This time I want to
look at the module which actually looks at the data being passed and will
convert all output to upper case letters. Basically, it will do the same thing
as tr a-z A-Z command does, but using STREAMS
upmod_upcase() function that
examines all M_DATA messages in the mblk and converts every symbol to upper
case:
#define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
#define isupper(x) (((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z'))
#define toupper(x) (isupper(x) ? (x) : (unsigned)(x) - 'a' + 'A')
/*
* Convert all ASCII chars in data blocks to upper case
*/
static mblk_t *
upmod_upcase(mblk_t *passed_mp)
{
mblk_t *mp = passed_mp;
for (; mp != NULL; mp = mp->b_cont) {
if ((DB_TYPE(mp) == M_DATA) && (MBLKL(mp) > 0)) {
unsigned char *p;
for (p = mp->b_rptr; p < mp->b_wptr; p++)
if (islower(*p))
*p = toupper(*p);
}
}
return (passed_mp);
}
The DB_TYPE(mp) macro simply returns
mp->b_datab->db_type value and MBLKL(mp) is the
amount of data between the read and write pointers . These macros together
with some other useful definitions are defined in
sys/strsun.h file:
#define DB_BASE(mp) ((mp)->b_datap->db_base) #define DB_LIM(mp) ((mp)->b_datap->db_lim) #define DB_REF(mp) ((mp)->b_datap->db_ref) #define DB_TYPE(mp) ((mp)->b_datap->db_type) #define MBLKL(mp) ((mp)->b_wptr - (mp)->b_rptr) #define MBLKSIZE(mp) ((mp)->b_datap->db_lim - (mp)->b_datap->db_base) #define MBLKHEAD(mp) ((mp)->b_rptr - (mp)->b_datap->db_base) #define MBLKTAIL(mp) ((mp)->b_datap->db_lim - (mp)->b_wptr) #define MBLKIN(mp, off, len) (((off) <= MBLKL(mp)) && \ (((mp)->b_rptr + (off) + (len)) <= (mp)->b_wptr))
Now we can modify the read-side put procedure to call
upmod_upcase for each mblock seen on input.
static void
upmodrput(queue_t *q, mblk_t *mp)
{
upmodput(q, upmod_upcase(mp));
}
Here is the full example:
/* * This example demonstrates a minimum STREAMS module that honors flow control. * It converts all data bytes on the read side to the upper case. */ /* * Required include files. */ #include#include #include #include #include #include #include /* * Function prototypes. */ static int upmodopen(queue_t *, dev_t *, int, int, cred_t *); static int upmodclose(queue_t *); static void upmodput(queue_t *, mblk_t *); static void upmodrput(queue_t *, mblk_t *); static void upmodsrv(queue_t *); static mblk_t *upmod_upcase(mblk_t *mp); /* * Module linkage data */ static struct module_info upmod_minfo = { 2, /* mi_idnum */ "upmod", /* mi_idname */ 0, /* mi_minpsz */ INFPSZ, /* mi_maxpsz */ 4096, /* mi_hiwat */ 512 /* mi_lowat */ }; static struct qinit upmod_rinit = { (int (*)())upmodput, /* qi_putp */ (int (*)())upmodsrv, /* qi_srvp */ upmodopen, /* qi_qopen */ upmodclose, /* qi_qclose */ NULL, /* qi_qadmin */ &upmod_minfo, /* qi_minfo */ }; static struct qinit upmod_winit = { (int (*)())upmodrput, /* qi_putp */ (int (*)())upmodsrv, /* qi_srvp */ NULL, /* qi_qopen */ NULL, /* qi_qclose */ NULL, /* qi_qadmin */ &upmod_minfo, /* qi_minfo */ }; static struct streamtab upmod_info = { &upmod_rinit, /* st_rdinit */ &upmod_winit, /* st_wrinit */ }; static struct fmodsw fsw = { "upmod", &upmod_info, D_MP | D_MTPERQ }; /* * Module linkage information for the kernel. */ struct mod_ops mod_strmodops; static struct modlstrmod modlstrmod = { &mod_strmodops, "Example up-through module 1.0", &fsw }; static struct modlinkage modlinkage = { MODREV_1, (void *)&modlstrmod, NULL }; /* * Standard module entry points. */ int _init(void) { return (mod_install(&modlinkage)); } int _fini(void) { return (mod_remove(&modlinkage)); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } /* * Actual module code. */ /* * STREAMS entry points. */ /* ARGSUSED */ static int upmodopen(queue_t *rq, dev_t *dev, int oflag, int sflag, cred_t *crp) { if (sflag != MODOPEN) return (EINVAL); /* Prevent duplicate opens */ if (rq->q_ptr != NULL) return (0); rq->q_ptr = WR(rq)->q_ptr = (void *)1; qprocson(rq); /* * At this point module is linked in the STREAM and can send/receive * messages. Its put/service procedures may execute at any time. */ return (0); } static int upmodclose(queue_t *rq) { qprocsoff(rq); rq->q_ptr = WR(rq)->q_ptr = NULL; /* * At this point module is disconnected from the STREAM and can * no longer receive messages. Its put or service procedures are not * running. */ return (0); } /* * Support routines. */ /* Put procedure */ static void upmodput(queue_t *q, mblk_t *mp) { /* * If the message is a high-priority message or there is no flow control * and there are no messages in the queue already, pass it forward, * otherwise queue. */ if (queclass(mp) == QPCTL || ((q->q_first == NULL) && canputnext(q))) putnext(q, mp); else (void) putq(q, mp); } /* * Support routines. */ static void upmodrput(queue_t *q, mblk_t *mp) { upmodput(q, upmod_upcase(mp)); } /* Read/write side service routine */ static void upmodsrv(queue_t *q) { mblk_t *mp; /* * Get messages from the service queue and pass them forward until flow * controlled. */ while ((mp = getq(q)) != NULL) { if (canputnext(q)) { putnext(q, mp); } else { (void) putbq(q, mp); break; } } } #ifndef islower #define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z')) #endif #ifndef isupper #define isupper(x) (((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z')) #endif #ifndef toupper #define toupper(x) (isupper(x) ? (x) : (unsigned)(x) - 'a' + 'A') #endif /* * Convert all ASCII chars in data blocks to upper case */ static mblk_t * upmod_upcase(mblk_t *passed_mp) { mblk_t *mp = passed_mp; for (; mp != NULL; mp = mp->b_cont) { if ((DB_TYPE(mp) == M_DATA) && (MBLKL(mp) > 0)) { uchar_t *p; for (p = mp->b_rptr; p < mp->b_wptr; p++) if (islower(*p)) *p = toupper(*p); } } return (passed_mp); }
Let us save it in file upmod.c, and compile it:
$ /usr/sfw/bin/gcc -c -m64 -o upmod.o -I/usr/include \
-O -D_KERNEL -D_SYSCALL32 -D_SYSCALL32_IMPL upmod.c
$ ld -r -o upmod upmod.o
Now we can install it. For example, on sparc system:
$ su
# cp upmod /kernel/strmod/sparcv9
# exit
$ strchg -h upmod
$ UPTIME
4:53PM UP 35 DAY(S), 4:38, 2 USERS, LOAD AVERAGE: 0.01, 0.01, 0.00
USER TTY LOGIN@ IDLE JCPU PCPU WHAT
USER PTS/1 8AUG0510DAYS 8 BASH
USER PTS/2 4:11PM 12 1 W
$ STRCHG -P
$ w
4:55pm up 35 day(s), 4:39, 2 users, load average: 0.00, 0.01, 0.00
User tty login@ idle JCPU PCPU what
user pts/1 8Aug0510days 8 bash
user pts/2 4:11pm 12 1 w
The output reminds me of the old Russian-made mainframes ES 1045 (Soviet clone of IBM 360/370 series) which could only output in all caps. Interestingly, at these times Russian-made computers usually used capital letters for English and lower letters for Russian. This was a precursor for KOI8-r encoding.