見出し画像

NET-SNMPのソースコードからMIBの値をうまく表示する処理の写経をした

今朝は5時半から開発開始です。最近は新人助手の猫さんが起きる時間を完全にコントロールしています。かなり優秀な開発助手です。

さて、

の問題のチケットに対応するために、NET-SNMPのソースコードを、真面目に読んでみました。ソースコードは、GitHUBに移転してきたようで入手しやすくなっていました。

この中で、整数をDISPLAY-HINTに対応して表示する処理を探しました。

/**
 * Prints an integer according to the hint into a buffer.
 *
 * If allow_realloc is true the buffer will be (re)allocated to fit in the 
 * needed size. (Note: *buf may change due to this.)
 * 
 * @param buf      Address of the buffer to print to.
 * @param buf_len  Address to an integer containing the size of buf.
 * @param out_len  Incremented by the number of characters printed.
 * @param allow_realloc if not zero reallocate the buffer to fit the 
 *                      needed size.
 * @param val      The variable to encode.
 * @param decimaltype 'd' or 'u' depending on integer type
 * @param hint     Contents of the DISPLAY-HINT clause of the MIB.
 *                 See RFC 1903 Section 3.1 for details. may _NOT_ be NULL.
 * @param units    Contents of the UNITS clause of the MIB. may be NULL.
 * 
 * @return 1 on success, or 0 on failure (out of memory, or buffer to
 *         small when not allowed to realloc.)
 */
int
sprint_realloc_hinted_integer(u_char ** buf, size_t * buf_len,
                              size_t * out_len, int allow_realloc,
                              long val, const char decimaltype,
                              const char *hint, const char *units)
{
    char            fmt[10] = "%l@", tmp[256];
    int             shift = 0, len, negative = 0;

    if (!strchr("bdoux", decimaltype)) {
        snmp_log(LOG_ERR, "Invalid decimal type '%c'\n", decimaltype);
        return 0;
    }

    switch (hint[0]) {
    case 'd':
        /*
         * We might *actually* want a 'u' here.  
         */
        if (hint[1] == '-') {
            shift = atoi(hint + 2);
            if (shift < 0)
                shift = 0;
        }
        fmt[2] = decimaltype;
        if (val < 0) {
            negative = 1;
            val = -val;
        }
        snprintf(tmp, sizeof(tmp), fmt, val);
        break;
    case 'o':
    case 'x':
        fmt[2] = hint[0];
        snprintf(tmp, sizeof(tmp), fmt, val);
        break;
    case 'b': {
	unsigned long int bit = 0x80000000LU;
	char *bp = tmp;
	while (bit) {
	    *bp++ = val & bit ? '1' : '0';
	    bit >>= 1;
	}
	*bp = 0;
        break;
    }
    default:
        return 0;
    }

    if (shift != 0) {
        len = strlen(tmp);
        if (shift <= len) {
            tmp[len + 1] = 0;
            while (shift--) {
                tmp[len] = tmp[len - 1];
                len--;
            }
            tmp[len] = '.';
        } else if (shift < sizeof(tmp) - 1) {
            tmp[shift + 1] = 0;
            while (shift) {
                if (len-- > 0) {
                    tmp[shift] = tmp[len];
                } else {
                    tmp[shift] = '0';
                }
                shift--;
            }
            tmp[0] = '.';
        }
    }
    if (negative) {
        len = strlen(tmp)+1;
        while (len) {
            tmp[len] = tmp[len-1];
            len--;
        }
        tmp[0] = '-';
    }
    return snmp_cstrcat(buf, buf_len, out_len, allow_realloc, tmp);
}

という関数でした。久しぶりにC言語のソースコードを読むと戸惑うことが多いです。なんとか読み解いてGO言語で書いてみました。

func printHintedInt(val int32, hint string, us bool) string {
	if hint == "" {
		if us {
			return fmt.Sprintf("%d", uint32(val))
		}
		return fmt.Sprintf("%d", val)
	}
	h := hint[0:1]
	switch h {
	case "d":
		r := ""
		n := false
		if us {
			r = fmt.Sprintf("%d", uint32(val))
		} else {
			if val < 0 {
				n = true
				val = -val
			}
			r = fmt.Sprintf("%d", val)
		}
		if len(hint) > 2 && hint[1:2] == "-" {
			s, err := strconv.Atoi(hint[2:])
			if err == nil && s != 0 {
				if s <= len(r) {
					r = r[0:s] + "." + r[s:]
				} else {
					tmp := "."
					for len(tmp) < s-len(r)+1 {
						tmp += "0"
					}
					r = tmp + r
				}
			}
		}
		if n {
			r = "-" + r
		}
		return r
	case "x":
		return fmt.Sprintf("%x", val)
	case "o":
		return fmt.Sprintf("%o", val)
	case "b":
		r := ""
		for b := 0x80000000; b != 0; b >>= 1 {
			if int32(b)&val != 0 {
				r += "1"
			} else {
				r += "0"
			}
		}
		return r
	}
	return ""
}

ちょっと短くなりました。簡単にテストした感じでは問題なさそうです。
TWSNMP FCとシン・TWSNMPに組み込んでみました。でも、この処理は、ほとんど使われないようです。
TEXTUAL-CONVENTIONで定義したデータの型のEnumも対応しました。

とりあえず、TWSNMP FCのDocker版を組み込んだものに更新しました。

のように表示できています。

明日に続く

開発のための諸経費(機材、Appleの開発者、サーバー運用)に利用します。 ソフトウェアのマニュアルをnoteの記事で提供しています。 サポートによりnoteの運営にも貢献できるのでよろしくお願います。