*** wmmon.c.orig Tue May 19 16:13:16 1998 --- wmmon.c Fri Dec 31 12:05:34 1999 *************** *** 28,33 **** --- 28,44 ---- Changes: ---- + 12/01/1999 (Stephen Kiernan, sk-ports@vegamuse.org) + * Change to revert to real userid and groupid + after kvm_openfiles is accomplished. + (Patch from Steve Reid, sreid@sea-to-sky.net) + 05/24/1999 (Stephen Kiernan, sk-ports@vegamuse.org) + * Ported to FreeBSD 4.0 + 12/11/1998 (Stephen Kiernan, sk-ports@vegamuse.org) + * Ported to FreeBSD 2.2, 3.0 + * Based on code from the FreeBSD 2.2 version of top + by Christos Zoulas, Steven Wallace, and + Wolfram Schneider 18/05/1998 (Antoine Nulle, warp@xs4all.nl) * MEM/SWAP/UPTIME only updated when visible * Using global file descriptors to reduce file *************** *** 72,81 **** --- 83,109 ---- #include #include + #include + #include #include #include + #include + #include + #include + #include + #include + #if __FreeBSD_version >= 300000 + #include + #endif + #include + #include + #if __FreeBSD_version >= 400000 && __FreeBSD_version < 400005 + #include + #endif + #include + #include #include #include *************** *** 106,117 **** --- 134,193 ---- FILE *fp_stat; FILE *fp_loadavg; + kvm_t *kvmd = NULL; + struct nlist nl[] = { + #define N_CNT 0 + { "_cnt" }, + #define N_BUFSPACE 1 + { "_bufspace" }, + #define N_CP_TIME 2 + { "_cp_time" }, + #define N_AVERUN 3 + { "_averunnable" }, + #if __FreeBSD_version >= 300000 + #define N_TK_NIN 4 + { "_tk_nin" }, + #define N_TK_NOUT 5 + { "_tk_nout" }, + #else + #define N_DK_NDRIVE 4 + { "_dk_ndrive" }, + #define N_DK_WDS 5 + { "_dk_wds" }, + #endif + #if __FreeBSD_version < 400000 + #define VM_SWAPLIST 6 + { "_swaplist" }, + #define VM_SWDEVT 7 + { "_swdevt" }, + #define VM_NSWAP 8 + { "_nswap" }, + #define VM_NSWDEV 9 + { "_nswdev" }, + #define VM_DMMAX 10 + { "_dmmax" }, + #endif + { "" } + }; + int psize; + int pshift; + long *cur_dk_wds; + long *last_dk_wds; + int ndrives; + char errbuf[_POSIX2_LINE_MAX]; + static int swappgsin = -1; + static int swappgsout = -1; + /* functions */ void usage(void); void printversion(void); void DrawStats(int *, int, int, int, int); void DrawStats_io(int *, int, int, int, int); + #if defined(__FreeBSD__) || defined(__NetBSD__) + int swapmode( long *retavail, long *retfree); + #endif /* __FreeBSD__ || __NetBSD__ */ + void wmmon_routine(int, char **); void main(int argc, char *argv[]) { *************** *** 154,159 **** --- 230,241 ---- } } + if( checkversion() < 0 ) + { + fprintf( stderr, devstat_errbuf ); + exit(1); + } + wmmon_routine(argc, argv); } *************** *** 213,238 **** long istat; long idle; ! FILE *fp; ! char temp[128]; char *p; ! int xpm_X = 0, xpm_Y = 0; long online_time = 0; long ref_time = 0; long cnt_time; ! ! fp = fopen("/proc/uptime", "r"); ! fp_meminfo = fopen("/proc/meminfo", "r"); ! fp_loadavg = fopen("/proc/loadavg", "r"); ! fp_stat = fopen("/proc/stat", "r"); ! ! if (fp) { ! fscanf(fp, "%ld", &online_time); ref_time = time(0); ! fclose(fp); } for (i=0; i1; pshift++, psize>>=1) ! continue; ! pshift -= 10; ! psize = getpagesize(); ! ! if (setgid(getgid()) != 0) exit(1); /* We're sgid kmem. Give up privs. */ ! if (setuid(getuid()) != 0) exit(1); /* If we're suid, give that up too. */ ! ! if (kvmd==NULL) kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); ! if (kvmd==NULL) { fprintf(stderr, "kvm_openfiles: %s\n", errbuf); exit(errno); } ! ! /* We're sgid kmem. Give up privs. */ ! if (setgid(getgid()) != 0) { perror("setgid"); exit(errno); } ! ! /* If we're suid, give that up too. */ ! if (setuid(getuid()) != 0) { perror("seguid"); exit(errno); } ! ! if (kvmd) { ! if (kvm_nlist(kvmd, nl) >= 0) { ! struct nlist *nlp; ! ! for( nlp = nl; *nlp->n_name != '\0'; nlp++ ) { ! if( nlp->n_type == 0 ) ! fprintf (stderr, "kvm_nlist: Symbol '%s' not found\n", nlp->n_name); ! } ! ! #if __FreeBSD_version >= 300000 ! ndrives = getnumdevs(); ! #else ! if (nl[0].n_type != 0) { ! (void) kvm_read(kvmd, nl[N_DK_NDRIVE].n_value, (char *)&ndrives, sizeof(ndrives)); ! if (ndrives > 0) { ! cur_dk_wds = calloc(ndrives, sizeof(long)); ! last_dk_wds = calloc(ndrives, sizeof(long)); ! bzero(last_dk_wds, sizeof(long) * ndrives); ! } ! #endif ! fprintf (stderr, "Monitoring %d devices for activity.\n", ndrives); ! #if __FreeBSD_version < 300000 ! } ! #endif ! } ! else { ! fprintf( stderr, "kvm_nlist: %s\n", kvm_geterr(kvmd) ); ! } } for (i=0; irt_idle = idle - st->idlelast; st->idlelast = idle; + /* There's a problem here with values crossing + the max long size barrier becoming negative -- + things restabilize after some time, but a + better solution needs to be designed + */ st->rt_stat = istat - st->statlast; st->statlast = istat; *************** *** 499,543 **** void update_stat_mem(stat_dev *st, stat_dev *st2) { ! char temp[128]; ! unsigned long free, shared, buffers, cached; ! freopen("/proc/meminfo", "r", fp_meminfo); ! while (fgets(temp, 128, fp_meminfo)) { ! if (strstr(temp, "Mem:")) { ! sscanf(temp, "Mem: %ld %ld %ld %ld %ld %ld", ! &st->rt_idle, &st->rt_stat, ! &free, &shared, &buffers, &cached); ! st->rt_idle >>= 10; ! st->rt_stat -= buffers+cached; ! st->rt_stat >>= 10; ! // break; ! } ! if (strstr(temp, "Swap:")) { ! sscanf(temp, "Swap: %ld %ld", &st2->rt_idle, &st2->rt_stat); ! st2->rt_idle >>= 10; ! st2->rt_stat >>= 10; ! break; } } } void update_stat_swp(stat_dev *st) { ! char temp[128]; ! fseek(fp_meminfo, 0, SEEK_SET); ! while (fgets(temp, 128, fp_meminfo)) { ! if (strstr(temp, "Swap:")) { ! sscanf(temp, "Swap: %ld %ld", &st->rt_idle, &st->rt_stat); ! st->rt_idle >>= 10; ! st->rt_stat >>= 10; ! break; } } } /*******************************************************************************\ |* get_statistics *| \*******************************************************************************/ --- 636,890 ---- void update_stat_mem(stat_dev *st, stat_dev *st2) { ! unsigned long buffers; ! if (kvmd==NULL) kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); ! if (kvmd==NULL) { fprintf(stderr, "kvm_openfiles: %s\n", errbuf); exit(errno); } ! if (kvmd) { ! if (kvm_nlist(kvmd, nl) >= 0) { ! if (nl[0].n_type != 0) { ! static int first_time_done = 0; ! int dpagein, dpageout; ! struct vmmeter sum; ! ! if ((kvm_read(kvmd, nl[N_CNT].n_value, (char *)&sum, sizeof(sum))==sizeof(sum)) && ! (kvm_read(kvmd, nl[N_BUFSPACE].n_value, (char *)&buffers, sizeof(buffers))==sizeof(buffers))) { ! st->rt_idle = (sum.v_page_count - (buffers/psize) - sum.v_wire_count) << pshift; ! st->rt_stat = sum.v_active_count << pshift; ! ! if (swappgsin < 0) { ! dpagein = 0; ! dpageout = 0; ! } ! else { ! dpagein = (sum.v_swappgsin - swappgsin) << pshift; ! dpageout = (sum.v_swappgsout - swappgsout) << pshift; ! } ! swappgsin = sum.v_swappgsin; ! swappgsout = sum.v_swappgsout; ! ! if ((dpagein>0) || (dpageout>0) || (first_time_done==0)) { ! swapmode(&st2->rt_idle, &st2->rt_stat); ! st2->rt_stat = st2->rt_idle - st2->rt_stat; ! } ! first_time_done = 1; ! } ! } } } } void update_stat_swp(stat_dev *st) { ! if (kvmd==NULL) kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); ! if (kvmd==NULL) { fprintf(stderr, "kvm_openfiles: %s\n", errbuf); exit(errno); } ! if (kvmd) { ! if (kvm_nlist(kvmd, nl) >= 0) { ! if (nl[0].n_type != 0) { ! static int first_time_done = 0; ! int dpagein, dpageout; ! ! struct vmmeter sum; ! if (kvm_read(kvmd, nl[N_CNT].n_value, (char *)&sum, sizeof(sum))==sizeof(sum)) { ! if (swappgsin < 0) { ! dpagein = 0; ! dpageout = 0; ! } ! else { ! dpagein = (sum.v_swappgsin - swappgsin) << pshift; ! dpageout = (sum.v_swappgsout - swappgsout) << pshift; ! } ! swappgsin = sum.v_swappgsin; ! swappgsout = sum.v_swappgsout; ! ! if ((dpagein>0) || (dpageout>0) || (first_time_done==0)) { ! swapmode(&st->rt_idle, &st->rt_stat); ! st->rt_stat = st->rt_idle - st->rt_stat; ! } ! first_time_done = 1; ! } ! } ! } ! } ! } ! ! #if __FreeBSD_version < 400000 ! /* ! * swapmode for FreeBSD 2.x and 3.x is based on a program called swapinfo ! * written by Kevin Lahey . ! */ ! ! #define SVAR(var) __STRING(var) /* to force expansion */ ! #define KGET(idx, var) \ ! KGET1(idx, &var, sizeof(var), SVAR(var)) ! #define KGET1(idx, p, s, msg) \ ! KGET2(nl[idx].n_value, p, s, msg) ! #define KGET2(addr, p, s, msg) \ ! if (kvm_read(kvmd, (u_long)(addr), p, s) != s) { \ ! return (0); \ ! } ! #define KGETRET(addr, p, s, msg) \ ! if (kvm_read(kvmd, (u_long)(addr), p, s) != s) { \ ! return (0); \ ! } ! #endif ! ! ! int swapmode( long *retavail, long *retfree) ! { ! int used, avail; ! #if __FreeBSD_version >= 400000 ! struct kvm_swap kvmswap; ! #else ! char *header; ! int hlen, nswap, nswdev, dmmax; ! int i, div, nfree, npfree; ! struct swdevt *sw; ! long blocksize, *perdev; ! u_long ptr; ! struct rlist head; ! # if __FreeBSD_version >= 220000 ! struct rlisthdr swaplist; ! # else ! struct rlist *swaplist; ! # endif ! struct rlist *swapptr; ! #endif ! ! /* ! * Counter for error messages. If we reach the limit, ! * stop reading information from swap devices and ! * return zero. This prevent endless 'bad address' ! * messages. ! */ ! static int warning = 10; ! ! if (warning <= 0) { ! /* a single warning */ ! if (!warning) { ! warning--; ! fprintf(stderr, ! "Too much errors, stop reading swap devices ...\n"); ! (void)sleep(3); ! } ! return(0); ! } ! warning--; /* decrease counter, see end of function */ ! ! #if __FreeBSD_version >= 400000 ! if( kvm_getswapinfo( kvmd, &kvmswap, 1, 0 ) < 0 ) { ! fprintf(stderr, "kvm_getswapinfo failed\n"); ! return(0); ! } ! ! *retavail = avail = kvmswap.ksw_total; ! used = kvmswap.ksw_used; ! *retfree = kvmswap.ksw_total - used; ! #else ! KGET(VM_NSWAP, nswap); ! if (!nswap) { ! fprintf(stderr, "No swap space available\n"); ! return(0); ! } ! ! KGET(VM_NSWDEV, nswdev); ! KGET(VM_DMMAX, dmmax); ! KGET1(VM_SWAPLIST, &swaplist, sizeof(swaplist), "swaplist"); ! if ((sw = (struct swdevt *)malloc(nswdev * sizeof(*sw))) == NULL || ! (perdev = (long *)malloc(nswdev * sizeof(*perdev))) == NULL) ! { ! perror("malloc"); ! exit(1); ! } ! KGET1(VM_SWDEVT, &ptr, sizeof ptr, "swdevt"); ! KGET2(ptr, sw, nswdev * sizeof(*sw), "*swdevt"); ! /* Count up swap space. */ ! nfree = 0; ! memset(perdev, 0, nswdev * sizeof(*perdev)); ! #if __FreeBSD_version >= 220000 ! swapptr = swaplist.rlh_list; ! while (swapptr) { ! #else ! while (swaplist) { ! #endif ! int top, bottom, next_block; ! #if __FreeBSD_version >= 220000 ! KGET2(swapptr, &head, sizeof(struct rlist), "swapptr"); ! #else ! KGET2(swaplist, &head, sizeof(struct rlist), "swaplist"); ! #endif ! ! top = head.rl_end; ! bottom = head.rl_start; ! ! nfree += top - bottom + 1; ! ! /* ! * Swap space is split up among the configured disks. ! * ! * For interleaved swap devices, the first dmmax blocks ! * of swap space some from the first disk, the next dmmax ! * blocks from the next, and so on up to nswap blocks. ! * ! * The list of free space joins adjacent free blocks, ! * ignoring device boundries. If we want to keep track ! * of this information per device, we'll just have to ! * extract it ourselves. ! */ ! ! while (top / dmmax != bottom / dmmax) { ! next_block = ((bottom + dmmax) / dmmax); ! perdev[(bottom / dmmax) % nswdev] += ! next_block * dmmax - bottom; ! bottom = next_block * dmmax; } + perdev[(bottom / dmmax) % nswdev] += + top - bottom + 1; + + #if __FreeBSD_version >= 220000 + swapptr = head.rl_next; + #else + swaplist = head.rl_next; + #endif + } + + header = getbsize(&hlen, &blocksize); + div = blocksize / 512; + avail = npfree = 0; + for (i = 0; i < nswdev; i++) { + int xsize, xfree; + + /* + * Don't report statistics for partitions which have not + * yet been activated via swapon(8). + */ + + xsize = sw[i].sw_nblks; + xfree = perdev[i]; + used = xsize - xfree; + npfree++; + avail += xsize; } + /* + * If only one partition has been set up via swapon(8), we don't + * need to bother with totals. + */ + *retavail = avail / 2; + *retfree = nfree / 2; + used = avail - nfree; + free(sw); free(perdev); + #endif /* __FreeBSD_version >= 400000 */ + + /* increase counter, no errors occurs */ + warning++; + + return (int)(((double)used / (double)avail * 100.0) + 0.5); } + + /*******************************************************************************\ |* get_statistics *| \*******************************************************************************/ *************** *** 545,554 **** void get_statistics(char *devname, long *is, long *ds, long *idle) { int i; ! char temp[128]; ! char *p; ! char *tokens = " \t\n"; ! float f; long maxdiskio=0; *is = 0; --- 892,899 ---- void get_statistics(char *devname, long *is, long *ds, long *idle) { int i; ! long averun[3]; ! long cp_time[CPUSTATES]; long maxdiskio=0; *is = 0; *************** *** 556,592 **** *idle = 0; if (!strncmp(devname, "cpu", 3)) { ! fseek(fp_stat, 0, SEEK_SET); ! while (fgets(temp, 128, fp_stat)) { ! if (strstr(temp, "cpu")) { ! p = strtok(temp, tokens); ! /* 1..3, 4 == idle, we don't want idle! */ ! for (i=0; i<3; i++) { ! p = strtok(NULL, tokens); ! *ds += atol(p); ! } ! p = strtok(NULL, tokens); ! *idle = atol(p); ! } ! } ! fp_loadavg = freopen("/proc/loadavg", "r", fp_loadavg); ! fscanf(fp_loadavg, "%f", &f); ! *is = (long) (100 * f); } if (!strncmp(devname, "i/o", 3)) { ! fseek(fp_stat, 0, SEEK_SET); ! while (fgets(temp, 128, fp_stat)) { ! if (strstr(temp, "disk_rio") || strstr(temp, "disk_wio")) { ! p = strtok(temp, tokens); ! /* 1..4 */ ! for (i=0; i<4; i++) { ! p = strtok(NULL, tokens); ! *ds += atol(p); } } } if (*ds > maxdiskio) maxdiskio = *ds; } } --- 901,1105 ---- *idle = 0; if (!strncmp(devname, "cpu", 3)) { ! if (kvmd==NULL) kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); ! if (kvmd==NULL) { fprintf(stderr, "kvm_openfiles: %s\n", errbuf); exit(errno); } ! if (kvmd) { ! if (kvm_nlist(kvmd, nl) >= 0) { ! if (nl[0].n_type != 0) { ! if ((kvm_read(kvmd, nl[N_CP_TIME].n_value, (char *)&cp_time, sizeof(cp_time))==sizeof(cp_time)) && ! (kvm_read(kvmd, nl[N_AVERUN].n_value, (char *)&averun, sizeof(averun))==sizeof(averun))) { ! *is = (long) (100 * ((double)averun[0] / FSCALE)); ! ! for (i = 0; i < CPUSTATES; i++) { ! if (i != CP_IDLE) *ds += cp_time[i]; ! } ! *idle = cp_time[CP_IDLE]; ! } ! } ! } ! } } if (!strncmp(devname, "i/o", 3)) { + #if __FreeBSD_version < 300000 + if (kvmd==NULL) kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); + if (kvmd==NULL) { fprintf(stderr, "kvm_openfiles: %s\n", errbuf); exit(errno); } + if (kvmd) { + if (kvm_nlist(kvmd, nl) >= 0) { + if (nl[0].n_type != 0) { + if (kvm_read(kvmd, nl[N_DK_WDS].n_value, (char *)cur_dk_wds, ndrives * sizeof(long))==(ndrives*sizeof(long))) { + for (i = 0; i < ndrives; i++) { + *ds += cur_dk_wds[i]; + } + } + } + } + } + #else + static int initted = 0; + static struct statinfo last; + static struct statinfo cur; + int ndevs = getnumdevs(); + int gotdevs = 0; + long generation; + int num_devices_specified = 0; + int num_selected; + int num_selections; + int maxshowdevs = 10; + int num_matches = 0; + struct devstat_match *matches = NULL; + struct device_selection *dev_select = NULL; + char **specified_devices; + long select_generation; + + if( !initted ) + { + bzero( &cur, sizeof(cur) ); + bzero( &last, sizeof(cur) ); + + cur.dinfo = (struct devinfo *)malloc( sizeof(struct devinfo) ); + last.dinfo = (struct devinfo *)malloc( sizeof(struct devinfo) ); ! bzero( cur.dinfo, sizeof(struct devinfo) ); ! bzero( last.dinfo, sizeof(struct devinfo) ); ! ! specified_devices = (char **)malloc(sizeof(char *)); ! } ! else ! { ! struct devinfo *tmp; ! ! if (kvmd==NULL) kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); ! if (kvmd==NULL) ! { ! fprintf(stderr, "kvm_openfiles: %s\n", errbuf); ! exit(errno); ! } ! else ! { ! (void)kvm_read( kvmd, nl[N_TK_NIN].n_value, &cur.tk_nin, ! sizeof(cur.tk_nin) ); ! (void)kvm_read( kvmd, nl[N_TK_NOUT].n_value, &cur.tk_nout, ! sizeof(cur.tk_nout) ); ! (void)kvm_read( kvmd, nl[N_CP_TIME].n_value, ! cur.cp_time, sizeof(cur.cp_time) ); ! } ! ! tmp = last.dinfo; ! memcpy( &last, &cur, sizeof(cur) ); ! cur.dinfo = tmp; ! ! last.busy_time = cur.busy_time; ! } ! ! if( !gotdevs && ( getdevs( &cur ) >= 0 ) ) ! gotdevs = 1; ! ! if( gotdevs ) ! { ! generation = cur.dinfo->generation; ! ndevs = cur.dinfo->numdevs; ! ! if( !initted ) ! { ! selectdevs( &dev_select, &num_selected, &num_selections, ! &select_generation, generation, cur.dinfo->devices, ! ndevs, matches, num_matches, specified_devices, ! num_devices_specified, DS_SELECT_ONLY, maxshowdevs, ! 1 ); ! } ! else ! { ! struct devinfo *tmpinfo; ! long tmp; ! ! switch( getdevs( &cur ) ) ! { ! case 1: ! selectdevs( &dev_select, &num_selected, ! &num_selections, &select_generation, ! generation, cur.dinfo->devices, ! ndevs, matches, num_matches, ! specified_devices, ! num_devices_specified, ! DS_SELECT_ONLY, ! maxshowdevs, 1 ); ! ! if (kvmd==NULL) ! { ! fprintf(stderr, "kvm_openfiles: %s\n", errbuf); ! exit(errno); ! } ! else ! { ! (void)kvm_read( kvmd, nl[N_TK_NIN].n_value, &cur.tk_nin, ! sizeof(cur.tk_nin) ); ! (void)kvm_read( kvmd, nl[N_TK_NOUT].n_value, &cur.tk_nout, ! sizeof(cur.tk_nout) ); ! (void)kvm_read( kvmd, nl[N_CP_TIME].n_value, ! cur.cp_time, sizeof(cur.cp_time) ); ! } ! ! tmpinfo = last.dinfo; ! memcpy( &last, &cur, sizeof(cur) ); ! cur.dinfo = tmpinfo; ! ! last.busy_time = cur.busy_time; ! break; ! default: ! break; ! } ! ! selectdevs( &dev_select, &num_selected, &num_selections, ! &select_generation, generation, cur.dinfo->devices, ! ndevs, matches, num_matches, specified_devices, ! num_devices_specified, DS_SELECT_ONLY, maxshowdevs, ! 1 ); ! ! tmp = cur.tk_nin; ! cur.tk_nin -= last.tk_nin; ! last.tk_nin = tmp; ! tmp = cur.tk_nout; ! cur.tk_nout -= last.tk_nout; ! last.tk_nout = tmp; ! ! { ! register int dn; ! long double transfers_per_second; ! long double kb_per_transfer, mb_per_second; ! u_int64_t total_bytes, total_transfers, total_blocks; ! long double busy_seconds; ! long double blocks_per_second, ms_per_transaction; ! ! busy_seconds = compute_etime( cur.busy_time, last.busy_time ); ! ! for( dn = 0; dn < ndevs; dn++ ) ! { ! int di = dev_select[dn].position; ! ! if( compute_stats( &cur.dinfo->devices[di], ! &last.dinfo->devices[di], ! busy_seconds, ! &total_bytes, ! &total_transfers, ! &total_blocks, ! &kb_per_transfer, ! &transfers_per_second, ! &mb_per_second, ! &blocks_per_second, ! &ms_per_transaction ) == 0 ) ! { ! *ds += total_blocks; ! } ! } } } + initted = 1; } + + if( dev_select ) + free( dev_select ); + #endif if (*ds > maxdiskio) maxdiskio = *ds; } } *************** *** 597,605 **** int checksysdevs(void) { ! strcpy(stat_device[0].name, "cpu0"); ! strcpy(stat_device[1].name, "i/o"); ! strcpy(stat_device[2].name, "sys"); return 3; } --- 1110,1118 ---- int checksysdevs(void) { ! strncpy(stat_device[0].name, "cpu0", 5); ! strncpy(stat_device[1].name, "i/o", 5); ! strncpy(stat_device[2].name, "sys", 5); return 3; } *************** *** 638,643 **** --- 1151,1157 ---- int *p; int d; + /* printf ("size = %d, num = %d\n", size, num); */ pixels_per_byte = 100; p = his; for (j=0; j