commit a0090010f0f87ab972f8ddef6ae2aafc21988b66 Author: zzh <838331105@qq.com> Date: Thu Nov 6 10:32:18 2025 +0800 chore: initialize repository with .gitignore and README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b69be7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# Build artifacts +build/ +solo/build/ +**/.qt/ +**/.cmake/ +*.o +*.obj +*.so +*.a +*.dll +*.dylib +*.exe +*.out + +# Archives and packages +*.zip +*.tar +*.tar.gz +*.tgz +*.xz +*.7z + +# IDE/editor +.vscode/ +*.code-workspace +.trae/ + +# OS files +.DS_Store +Thumbs.db + +# Python +__pycache__/ +*.pyc + +# Misc +*.log diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fa05e45 --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +# Makefile for update_mac.c with ARM (aarch64) cross-compile, matching dev_gaiban compile.sh +# Usage: +# make # default: build arm64 +# make arm64 # build aarch64 binary using compile.sh toolchain +# make native # build native x86_64 binary (for local test) +# make toolchain # show cross compiler info +# make clean # remove binaries + +SHELL := /bin/bash + +COMPILE_SH ?= /home/hyx/work/dev_gaiban/APP_AP05_1.1.22_tuxi_shengchan/compile.sh +SRC := update_mac.c +TARGET_ARM64 := update_mac_pdd +TARGET_NATIVE := update_mac + +# Allow overriding CFLAGS via environment +CFLAGS ?= -O2 -Wall -I/home/hyx/work/pdd_shengchan/hiredis +LDFLAGS ?= -lpthread /home/hyx/work/pdd_shengchan/hiredis/libhiredis.a + +.PHONY: all arm64 native clean toolchain + +all: arm64 + +arm64: $(TARGET_ARM64) + +$(TARGET_ARM64): $(SRC) + . $(COMPILE_SH) && $${CC} $(CFLAGS) -o $@ $< $(LDFLAGS) + +native: $(TARGET_NATIVE) + +$(TARGET_NATIVE): $(SRC) + gcc $(CFLAGS) -o $@ $< $(LDFLAGS) + +# Show the configured cross toolchain +toolchain: + . $(COMPILE_SH) && echo "Using CC=$${CC}" && $${CC} -v || true + +clean: + rm -f $(TARGET_ARM64) $(TARGET_NATIVE) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ecdd6a8 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# pdd_shengchan + +本仓库用于拼多多生产工具相关代码与脚本的维护。 + +## 目录 +- `update_mac.c`:更新设备 MAC 的工具/逻辑。 +- `hiredis/`:Redis C 客户端依赖。 +- 其他目录按需补充。 + +## 开发 +- 使用 `Makefile` 进行编译(如有)。 +- 提交前确保不将构建产物和临时文件纳入版本控制。 + +## 许可 +未明确许可,默认内部使用。 diff --git a/hiredis b/hiredis new file mode 160000 index 0000000..2947820 --- /dev/null +++ b/hiredis @@ -0,0 +1 @@ +Subproject commit 294782041ef5841293dea3acc8ca9a28597bedc8 diff --git a/interfaces b/interfaces new file mode 100644 index 0000000..89ae5bf --- /dev/null +++ b/interfaces @@ -0,0 +1,11 @@ +# interfaces(5) file used by ifup(8) and ifdown(8) +# Include files from /etc/network/interfaces.d: +source-directory /etc/network/interfaces.d +auto lo +iface lo inet loopback +auto eth0 +iface eth0 inet static +address 10.10.12.12 +netmask 255.255.255.0 +gateway 10.10.12.1 +hwaddress ether 90:A9:F7:30:00:00 \ No newline at end of file diff --git a/update_mac.c b/update_mac.c new file mode 100644 index 0000000..170c4e4 --- /dev/null +++ b/update_mac.c @@ -0,0 +1,749 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hiredis.h" + +// 前置声明:黄灯控制接口 +static void start_yellow_blink(void); +static void stop_yellow_blink(void); +static void set_yellow_on(void); + +static int get_hw_mac_from_interfaces(const char* interfaces_path, char* out_mac, size_t out_size); + +static int is_hex(char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + +static void to_upper_str(char* s) { + for (; *s; ++s) *s = (char)toupper((unsigned char)*s); +} + +// 新增:检测是否包含显式MAC(带分隔符的形式),用于避免误把批次号当MAC +static int has_explicit_mac_pattern(const char* input) { + if (!input) return 0; + size_t n = strlen(input); + // 1) 冒号分隔 + for (size_t i = 0; i + 17 <= n; ++i) { + int ok = 1; + for (int seg = 0; seg < 6 && ok; ++seg) { + size_t pos = i + seg * 3; + if (!is_hex(input[pos]) || !is_hex(input[pos + 1])) { ok = 0; break; } + if (seg != 5) { if (input[pos + 2] != ':') { ok = 0; break; } } + } + if (ok) return 1; + } + // 2) 连字符分隔 + for (size_t i = 0; i + 17 <= n; ++i) { + int ok = 1; + for (int seg = 0; seg < 6 && ok; ++seg) { + size_t pos = i + seg * 3; + if (!is_hex(input[pos]) || !is_hex(input[pos + 1])) { ok = 0; break; } + if (seg != 5) { if (input[pos + 2] != '-') { ok = 0; break; } } + } + if (ok) return 1; + } + return 0; +} + +// Remove separators and validate exactly 12 hex chars +static int normalize_raw_mac(const char* in, char* raw12, size_t raw12_size) { + if (!in || !raw12 || raw12_size < 13) return -1; + size_t len = strlen(in); + size_t j = 0; + for (size_t i = 0; i < len; ++i) { + char c = in[i]; + if (c == ':' || c == '-' || c == ' ') continue; + if (!is_hex(c)) return -1; + if (j >= 12) return -1; // too long + raw12[j++] = c; + } + if (j != 12) return -1; + raw12[12] = '\0'; + return 0; +} + +static int format_mac_12_to_colon(const char* raw12, char* out, size_t out_size) { + if (!raw12 || !out || out_size < 18) return -1; // XX:XX:XX:XX:XX:XX\0 + char upper[13]; + strncpy(upper, raw12, 12); + upper[12] = '\0'; + to_upper_str(upper); + snprintf(out, out_size, "%c%c:%c%c:%c%c:%c%c:%c%c:%c%c", + upper[0], upper[1], upper[2], upper[3], upper[4], upper[5], + upper[6], upper[7], upper[8], upper[9], upper[10], upper[11]); + return 0; +} + +// Try to extract MAC from input string in various common formats +static int extract_mac(const char* input, char* out_mac, size_t out_size) { + if (!input || !out_mac || out_size < 18) return -1; + + size_t n = strlen(input); + + // 1) Look for colon-separated MAC: ([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2} + for (size_t i = 0; i + 17 <= n; ++i) { + int ok = 1; + for (int seg = 0; seg < 6 && ok; ++seg) { + size_t pos = i + seg * 3; + if (!is_hex(input[pos]) || !is_hex(input[pos + 1])) { ok = 0; break; } + if (seg != 5) { if (input[pos + 2] != ':') { ok = 0; break; } } + } + if (ok) { + // Copy and uppercase + strncpy(out_mac, input + i, 17); + out_mac[17] = '\0'; + to_upper_str(out_mac); + return 0; + } + } + + // 2) Look for hyphen-separated MAC: ([0-9A-Fa-f]{2}-){5}[0-9A-Fa-f]{2} + for (size_t i = 0; i + 17 <= n; ++i) { + int ok = 1; + for (int seg = 0; seg < 6 && ok; ++seg) { + size_t pos = i + seg * 3; + if (!is_hex(input[pos]) || !is_hex(input[pos + 1])) { ok = 0; break; } + if (seg != 5) { if (input[pos + 2] != '-') { ok = 0; break; } } + } + if (ok) { + char tmp[18]; + strncpy(tmp, input + i, 17); + tmp[17] = '\0'; + // Normalize to 12 hex then colon + char raw12[13]; + if (normalize_raw_mac(tmp, raw12, sizeof(raw12)) == 0 && + format_mac_12_to_colon(raw12, out_mac, out_size) == 0) { + return 0; + } + } + } + + // 3) JSON-like: "mac":"XXXXXXXXXXXX" or with colons + const char* key_variants[] = { "\"mac\"", "MAC", "mac", "Mac" }; + for (size_t kv = 0; kv < sizeof(key_variants)/sizeof(key_variants[0]); ++kv) { + const char* p = strstr(input, key_variants[kv]); + if (p) { + // find delimiter ':' then take next token until quote/space + p = strchr(p, ':'); + if (p) { + ++p; // at value start (maybe quotes) + while (*p == ' ' || *p == '"') ++p; + // copy until non-hex or separator + char rawbuf[64] = {0}; + size_t j = 0; + while (*p && j < sizeof(rawbuf)-1) { + if (is_hex(*p) || *p == ':' || *p == '-' ) { + rawbuf[j++] = *p; + ++p; + } else break; + } + rawbuf[j] = '\0'; + char raw12[13]; + if (normalize_raw_mac(rawbuf, raw12, sizeof(raw12)) == 0 && + format_mac_12_to_colon(raw12, out_mac, out_size) == 0) { + return 0; + } + } + } + } + + // 4) Fallback: first 12 consecutive hex chars + for (size_t i = 0; i < n; ++i) { + if (is_hex(input[i])) { + size_t j = 0; + char raw12[13]; + size_t k = i; + while (k < n && is_hex(input[k]) && j < 12) { + raw12[j++] = input[k++]; + } + if (j == 12) { + raw12[12] = '\0'; + if (format_mac_12_to_colon(raw12, out_mac, out_size) == 0) { + return 0; + } + } + } + } + + return -1; // not found +} + +// 新增:提取批次号(支持 batch=XXXX / Batch:XXXX / 扫码包含“批次”关键字) +static int extract_batch(const char* input, char* out_batch, size_t out_size) { + if (!input || !out_batch || out_size < 16) return -1; + size_t n = strlen(input); + // 主匹配:任意位置出现 'D' 或 'd' 后跟 14 位数字 + for (size_t i = 0; i + 15 <= n; ++i) { + char c = input[i]; + if (c == 'D' || c == 'd') { + int ok = 1; + for (int k = 1; k <= 14; ++k) { + char d = input[i + k]; + if (d < '0' || d > '9') { ok = 0; break; } + } + if (ok) { + out_batch[0] = 'D'; + memcpy(out_batch + 1, input + i + 1, 14); + out_batch[15] = '\0'; + return 0; + } + } + } + // 回退:按键名解析(batch/Batch/BATCH/批次) + const char* keys[] = {"batch", "Batch", "BATCH", "批次"}; + for (size_t i = 0; i < sizeof(keys)/sizeof(keys[0]); ++i) { + const char* p = strstr(input, keys[i]); + if (!p) continue; + // 跳过键名后的分隔符 + p += strlen(keys[i]); + while (*p == ' ' || *p == '=' || *p == ':' || (unsigned char)*p == 0xEF) { + if ((unsigned char)*p == 0xEF) { p++; if ((unsigned char)*p == 0xBC) { p++; if ((unsigned char)*p == 0x9A) { p++; } } } + else { p++; } + } + // 读取字母数字序列作为批次号 + size_t j = 0; + while (*p && j < out_size - 1) { + if ((*p >= '0' && *p <= '9') || (*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_' || *p == '-') { + out_batch[j++] = *p++; + } else { + break; + } + } + out_batch[j] = '\0'; + if (j > 0) { + // 规范化:若形如 d+14位数字,转为大写D + if (j >= 15 && (out_batch[0] == 'd' || out_batch[0] == 'D')) { + int ok2 = 1; + for (int k = 1; k <= 14; ++k) { + char d = out_batch[k]; + if (d < '0' || d > '9') { ok2 = 0; break; } + } + if (ok2) out_batch[0] = 'D'; + } + return 0; + } + } + + const char* p = strstr(input, "batch"); + if (p) { + p += 5; + size_t j = 0; + while (*p && j < out_size - 1 && ((*p >= '0' && *p <= '9') || (*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z'))) { + out_batch[j++] = *p++; + } + out_batch[j] = '\0'; + if (j > 0) { + if (j >= 15 && (out_batch[0] == 'd' || out_batch[0] == 'D')) { + int ok2 = 1; + for (int k = 1; k <= 14; ++k) { + char d = out_batch[k]; + if (d < '0' || d > '9') { ok2 = 0; break; } + } + if (ok2) out_batch[0] = 'D'; + } + return 0; + } + } + return -1; +} + +// 通过redis-cli查询批次映射得到MAC +static int redis_query_mac(const char* host_opt, const char* port_opt, const char* db_opt, + const char* pool_opt, const char* batch, char* out_mac, size_t out_size) { + if (!batch || !out_mac || out_size < 18) return -1; + + const char* mock = getenv("REDIS_MOCK_MAC"); + if (mock && *mock) { + fprintf(stderr, "[redis] use REDIS_MOCK_MAC=%s\n", mock); + if (extract_mac(mock, out_mac, out_size) == 0) return 0; + strncpy(out_mac, mock, out_size - 1); + out_mac[out_size - 1] = '\0'; + return 0; + } + + const char* host = host_opt ? host_opt : (getenv("REDIS_HOST") ? getenv("REDIS_HOST") : "180.163.74.83"); + int port = port_opt ? atoi(port_opt) : (getenv("REDIS_PORT") ? atoi(getenv("REDIS_PORT")) : 6379); + int db = db_opt ? atoi(db_opt) : (getenv("REDIS_DB") ? atoi(getenv("REDIS_DB")) : 0); + const char* pool = pool_opt ? pool_opt : (getenv("REDIS_POOL") ? getenv("REDIS_POOL") : "batch_sn_mapping_pdd"); + const char* auth = getenv("REDIS_AUTH"); + if (!auth || !*auth) auth = "Zzh08165511"; + + fprintf(stderr, "[redis] connect host=%s port=%d db=%d pool=%s batch=%s\n", host, port, db, pool, batch); + + struct timeval tv; tv.tv_sec = 3; tv.tv_usec = 0; + redisContext* c = redisConnectWithTimeout(host, port, tv); + if (!c || c->err) { + fprintf(stderr, "[redis] connect error: %s\n", c ? c->errstr : "unknown"); + if (c) redisFree(c); + return -1; + } + + if (auth && *auth) { + redisReply* ra = (redisReply*)redisCommand(c, "AUTH %s", auth); + if (!ra || (ra->type == REDIS_REPLY_ERROR)) { + fprintf(stderr, "[redis] AUTH error: %s\n", ra && ra->str ? ra->str : "no-reply"); + if (ra) freeReplyObject(ra); + redisFree(c); + return -1; + } + fprintf(stderr, "[redis] AUTH ok\n"); + freeReplyObject(ra); + } + + if (db > 0) { + redisReply* rs = (redisReply*)redisCommand(c, "SELECT %d", db); + if (!rs || (rs->type == REDIS_REPLY_ERROR)) { + fprintf(stderr, "[redis] SELECT %d error: %s\n", db, rs && rs->str ? rs->str : "no-reply"); + if (rs) freeReplyObject(rs); + redisFree(c); + return -1; + } + fprintf(stderr, "[redis] SELECT %d ok\n", db); + freeReplyObject(rs); + } + + // 尝试 HGET pool batch + redisReply* r = (redisReply*)redisCommand(c, "HGET %s %s", pool, batch); + if (r) { + fprintf(stderr, "[redis] HGET %s %s -> type=%d len=%ld\n", pool, batch, r->type, (long)(r->type==REDIS_REPLY_STRING ? r->len : 0)); + if (r->type == REDIS_REPLY_STRING && r->str && r->len > 0) { + fprintf(stderr, "[redis] HGET value: %.*s\n", (int)((r->len>128)?128:r->len), r->str); + if (extract_mac(r->str, out_mac, out_size) == 0) { freeReplyObject(r); redisFree(c); return 0; } + } + freeReplyObject(r); + } else { + fprintf(stderr, "[redis] HGET no-reply\n"); + } + + // 回退 GET pool:batch + r = (redisReply*)redisCommand(c, "GET %s:%s", pool, batch); + if (r) { + fprintf(stderr, "[redis] GET %s:%s -> type=%d len=%ld\n", pool, batch, r->type, (long)(r->type==REDIS_REPLY_STRING ? r->len : 0)); + if (r->type == REDIS_REPLY_STRING && r->str && r->len > 0) { + fprintf(stderr, "[redis] GET value: %.*s\n", (int)((r->len>128)?128:r->len), r->str); + if (extract_mac(r->str, out_mac, out_size) == 0) { freeReplyObject(r); redisFree(c); return 0; } + } + freeReplyObject(r); + } else { + fprintf(stderr, "[redis] GET no-reply\n"); + } + + fprintf(stderr, "[redis] not found or value not parsable as MAC\n"); + redisFree(c); + return -1; +} + +static int read_file(const char* path, char** out_buf, size_t* out_len) { + FILE* f = fopen(path, "rb"); + if (!f) return -1; + if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return -1; } + long sz = ftell(f); + if (sz < 0) { fclose(f); return -1; } + rewind(f); + char* buf = (char*)malloc((size_t)sz + 1); + if (!buf) { fclose(f); return -1; } + size_t n = fread(buf, 1, (size_t)sz, f); + fclose(f); + buf[n] = '\0'; + *out_buf = buf; + if (out_len) *out_len = n; + return 0; +} + +static int write_file(const char* path, const char* buf, size_t len) { + FILE* f = fopen(path, "wb"); + if (!f) return -1; + size_t n = fwrite(buf, 1, len, f); + // 确保数据写入到内核缓冲区 + fflush(f); + int rc = 0; + // 将数据与元数据刷新到磁盘,避免重启丢失 + int fd = fileno(f); + if (fd >= 0) { + if (fsync(fd) != 0) rc = -1; + } + fclose(f); + if (n != len) rc = -1; + return rc; +} + +static int update_hwaddress_in_interfaces(const char* interfaces_path, const char* mac_colon) { + // 如果当前MAC与目标一致,则不改写 + char current[64] = {0}; + if (get_hw_mac_from_interfaces(interfaces_path, current, sizeof(current)) == 0) { + to_upper_str(current); + if (strcmp(current, mac_colon) == 0) { + printf("MAC 未变化,跳过写入: %s\n", mac_colon); + return 0; + } + } + + const char* tmpl = + "# interfaces(5) file used by ifup(8) and ifdown(8)\n" + "# Include files from /etc/network/interfaces.d:\n" + "source-directory /etc/network/interfaces.d\n" + "auto lo\n" + "iface lo inet loopback\n" + "auto eth0\n" + "iface eth0 inet static\n" + "address 10.10.12.12\n" + "netmask 255.255.255.0\n" + "gateway 10.10.12.1\n" + "hwaddress ether %s\n"; + + char* newbuf = (char*)malloc(1024); + if (!newbuf) { + fprintf(stderr, "malloc failed\n"); + return -1; + } + int n = snprintf(newbuf, 1024, tmpl, mac_colon); + if (n <= 0) { + free(newbuf); + return -1; + } + + int rc = write_file(interfaces_path, newbuf, (size_t)n); + if (rc == 0) { + sync(); + printf("写入完整 interfaces 文件并更新MAC: %s\n", mac_colon); + } else { + fprintf(stderr, "写入失败 %s\n", interfaces_path); + } + + free(newbuf); + return rc; +} + +static int get_hw_mac_from_interfaces(const char* interfaces_path, char* out_mac, size_t out_size) { + char* content = NULL; size_t len = 0; + if (read_file(interfaces_path, &content, &len) != 0 || !content) { + return -1; + } + const char* needle = "hwaddress ether"; + char* pos = strstr(content, needle); + if (!pos) { free(content); return -1; } + pos += strlen(needle); + while (*pos == ' ') ++pos; + size_t j = 0; + while (*pos && *pos != '\n' && j < out_size - 1) { + if (*pos == ' ' || *pos == '\r' || *pos == '\t') break; + out_mac[j++] = *pos++; + } + out_mac[j] = '\0'; + free(content); + return j > 0 ? 0 : -1; +} + +static int scanner_read_event2(char* out, size_t out_size) { + const char* force_stdin = getenv("SCANNER_FORCE_STDIN"); + if (force_stdin && strcmp(force_stdin, "1") == 0) return -1; + + int fd = open("/dev/input/event2", O_RDONLY); + if (fd < 0) return -1; + struct input_event ev; + size_t j = 0; + int shift = 0; + while (1) { + ssize_t r = read(fd, &ev, sizeof(ev)); + if (r <= 0) continue; + if (ev.type != EV_KEY) continue; + + // 维护Shift状态 + if (ev.code == KEY_LEFTSHIFT || ev.code == KEY_RIGHTSHIFT) { + if (ev.value == 1) shift = 1; // 按下 + else if (ev.value == 0) shift = 0; // 松开 + continue; + } + + // 只在松开(key up)时采集字符,避免重复 + if (ev.value != 0) { + if (ev.code == KEY_ENTER || ev.code == KEY_KPENTER) { + break; + } + continue; + } + + char ch = 0; + switch (ev.code) { + // 数字键行 + case KEY_1: ch = shift ? '!' : '1'; break; + case KEY_2: ch = shift ? '@' : '2'; break; + case KEY_3: ch = shift ? '#' : '3'; break; + case KEY_4: ch = shift ? '$' : '4'; break; + case KEY_5: ch = shift ? '%' : '5'; break; + case KEY_6: ch = shift ? '^' : '6'; break; + case KEY_7: ch = shift ? '&' : '7'; break; + case KEY_8: ch = shift ? '*' : '8'; break; + case KEY_9: ch = shift ? '(' : '9'; break; + case KEY_0: ch = shift ? ')' : '0'; break; + // 小键盘数字 + case KEY_KP1: ch = '1'; break; case KEY_KP2: ch = '2'; break; case KEY_KP3: ch = '3'; break; + case KEY_KP4: ch = '4'; break; case KEY_KP5: ch = '5'; break; case KEY_KP6: ch = '6'; break; + case KEY_KP7: ch = '7'; break; case KEY_KP8: ch = '8'; break; case KEY_KP9: ch = '9'; break; + case KEY_KP0: ch = '0'; break; + + // 字母(显式映射,避免假设键码连续) + case KEY_A: ch = shift ? 'A' : 'a'; break; + case KEY_B: ch = shift ? 'B' : 'b'; break; + case KEY_C: ch = shift ? 'C' : 'c'; break; + case KEY_D: ch = shift ? 'D' : 'd'; break; + case KEY_E: ch = shift ? 'E' : 'e'; break; + case KEY_F: ch = shift ? 'F' : 'f'; break; + case KEY_G: ch = shift ? 'G' : 'g'; break; + case KEY_H: ch = shift ? 'H' : 'h'; break; + case KEY_I: ch = shift ? 'I' : 'i'; break; + case KEY_J: ch = shift ? 'J' : 'j'; break; + case KEY_K: ch = shift ? 'K' : 'k'; break; + case KEY_L: ch = shift ? 'L' : 'l'; break; + case KEY_M: ch = shift ? 'M' : 'm'; break; + case KEY_N: ch = shift ? 'N' : 'n'; break; + case KEY_O: ch = shift ? 'O' : 'o'; break; + case KEY_P: ch = shift ? 'P' : 'p'; break; + case KEY_Q: ch = shift ? 'Q' : 'q'; break; + case KEY_R: ch = shift ? 'R' : 'r'; break; + case KEY_S: ch = shift ? 'S' : 's'; break; + case KEY_T: ch = shift ? 'T' : 't'; break; + case KEY_U: ch = shift ? 'U' : 'u'; break; + case KEY_V: ch = shift ? 'V' : 'v'; break; + case KEY_W: ch = shift ? 'W' : 'w'; break; + case KEY_X: ch = shift ? 'X' : 'x'; break; + case KEY_Y: ch = shift ? 'Y' : 'y'; break; + case KEY_Z: ch = shift ? 'Z' : 'z'; break; + + // 其他符号 + case KEY_MINUS: ch = shift ? '_' : '-'; break; + case KEY_SEMICOLON: ch = shift ? ':' : ';'; break; + case KEY_APOSTROPHE: ch = shift ? '"' : '\''; break; + case KEY_BACKSLASH: ch = shift ? '|' : '\\'; break; + case KEY_EQUAL: ch = shift ? '+' : '='; break; + default: break; + } + + if (ch) { + if (j < out_size - 1) out[j++] = ch; + } + } + out[j] = '\0'; + close(fd); + return j > 0 ? 0 : -1; +} + +static int read_qrcode_string(char* out, size_t out_size) { + // Try hardware scanner first + printf("尝试读取硬件扫码内容...\n"); + if (scanner_read_event2(out, out_size) == 0) return 0; + // Fallback: read a line from stdin (useful for piping during tests) + fprintf(stdout, "请在终端输入扫码内容并回车...\n"); + if (fgets(out, (int)out_size, stdin) == NULL) return -1; + // trim newline + size_t n = strlen(out); + if (n && out[n-1] == '\n') out[n-1] = '\0'; + return strlen(out) > 0 ? 0 : -1; +} + +int main(int argc, char** argv) { + const char* interfaces_path = (argc >= 3) ? argv[2] : "/etc/network/interfaces"; + + if (argc < 2) { + // No scan input provided: check current MAC and if default, loop until scanned and updated + char current_mac[64] = {0}; + if (get_hw_mac_from_interfaces(interfaces_path, current_mac, sizeof(current_mac)) != 0) { + fprintf(stderr, "无法读取 %s 中的MAC\n", interfaces_path); + return 1; + } + to_upper_str(current_mac); + if (strcmp(current_mac, "90:A9:F7:30:00:00") == 0) { + fprintf(stdout, "检测到默认MAC %s,开始等待扫码...\n", current_mac); + start_yellow_blink(); + while (1) { + char scanbuf[512] = {0}; + if (read_qrcode_string(scanbuf, sizeof(scanbuf)) != 0) { + fprintf(stderr, "读取扫码内容失败,重试...\n"); + continue; + } + fprintf(stdout, "扫码原始内容: %s\n", scanbuf); + // 优先按批次从Redis查询目标MAC + char batch[128] = {0}; + char mac_from_redis[32] = {0}; + if (extract_batch(scanbuf, batch, sizeof(batch)) == 0) { + fprintf(stdout, "识别到批次号: %s,正在查询Redis...\n", batch); + if (redis_query_mac(NULL, NULL, NULL, NULL, batch, mac_from_redis, sizeof(mac_from_redis)) == 0) { + fprintf(stdout, "Redis返回MAC: %s\n", mac_from_redis); + to_upper_str(mac_from_redis); + if (strcmp(mac_from_redis, "90:A9:F7:30:00:00") == 0) { + fprintf(stderr, "Redis返回为默认MAC,请确认并重新扫码...\n"); + continue; + } + if (update_hwaddress_in_interfaces(interfaces_path, mac_from_redis) == 0) { + stop_yellow_blink(); + fprintf(stdout, "已更新MAC为: %s\n", mac_from_redis); + sync(); + char verify_mac[64] = {0}; + if (get_hw_mac_from_interfaces(interfaces_path, verify_mac, sizeof(verify_mac)) == 0) { + to_upper_str(verify_mac); + if (strcmp(verify_mac, "90:A9:F7:30:00:00") != 0) { + set_yellow_on(); + fprintf(stdout, "黄灯已常亮(MAC非默认)。\n"); + } else { + fprintf(stderr, "写入后仍为默认MAC,黄灯保持关闭或闪烁。\n"); + } + } + break; + } else { + fprintf(stderr, "写入失败,重试...\n"); + continue; + } + } else { + // Redis失败时,若仅有批次号且不含显式MAC,不再回退使用批次内容当MAC + if (!has_explicit_mac_pattern(scanbuf)) { + fprintf(stderr, "Redis未查询到该批次对应MAC或连接失败,且扫码内容不含显式MAC(如 12:34:56:78:9A:BC),请重新扫码或确保Redis可用。\n"); + continue; + } + fprintf(stderr, "Redis未查询到该批次对应MAC或连接失败,回退使用扫码中的MAC...\n"); + } + } + // 若未识别到批次号,给出提示 + if (extract_batch(scanbuf, batch, sizeof(batch)) != 0) { + fprintf(stderr, "未识别到批次号,回退使用扫码中的MAC...\n"); + } + // 回退:直接从扫码内容提取MAC(此处允许无分隔的12位HEX) + char mac_colon[32] = {0}; + if (extract_mac(scanbuf, mac_colon, sizeof(mac_colon)) != 0) { + fprintf(stderr, "未识别到有效MAC,重试...\n"); + continue; + } + to_upper_str(mac_colon); + if (strcmp(mac_colon, "90:A9:F7:30:00:00") == 0) { + fprintf(stderr, "扫描到默认MAC,请重新扫码...\n"); + continue; + } + if (update_hwaddress_in_interfaces(interfaces_path, mac_colon) == 0) { + set_yellow_on(); + fprintf(stdout, "已更新MAC为: %s\n", mac_colon); + break; + } else { + fprintf(stderr, "写入失败,重试...\n"); + } + } + return 0; + } else { + // 若非默认MAC,确保黄灯关闭 + set_yellow_on(); + fprintf(stdout, "当前MAC非默认(%s),无需更新。\n", current_mac); + return 0; + } + } + + // Legacy path: user provides scan string directly + fprintf(stderr, "Example: %s 'batch=202501;MAC=90A9F7F032E8' ./interfaces\n", argv[0]); + + const char* scan = argv[1]; + + // 先按批次从Redis查询 + char batch[128] = {0}; + char mac_from_redis[32] = {0}; + fprintf(stdout, "扫码原始内容: %s\n", scan); + if (extract_batch(scan, batch, sizeof(batch)) == 0) { + fprintf(stdout, "识别到批次号: %s,正在查询Redis...\n", batch); + if (redis_query_mac(NULL, NULL, NULL, NULL, batch, mac_from_redis, sizeof(mac_from_redis)) == 0) { + fprintf(stdout, "Redis返回MAC: %s\n", mac_from_redis); + if (update_hwaddress_in_interfaces(interfaces_path, mac_from_redis) != 0) { + return 3; + } + char verify_mac[64] = {0}; + if (get_hw_mac_from_interfaces(interfaces_path, verify_mac, sizeof(verify_mac)) == 0) { + to_upper_str(verify_mac); + if (strcmp(verify_mac, "90:A9:F7:30:00:00") != 0) { + set_yellow_on(); + fprintf(stdout, "黄灯已常亮(MAC非默认)。\n"); + } else { + fprintf(stderr, "写入后仍为默认MAC,黄灯保持关闭或闪烁。\n"); + } + } + return 0; + } else { + if (!has_explicit_mac_pattern(scan)) { + fprintf(stderr, "Redis未查询到该批次对应MAC或连接失败,且扫码内容不含显式MAC(如 12:34:56:78:9A:BC),请重新扫码或确保Redis可用。\n"); + return 2; + } + fprintf(stderr, "Redis未查询到该批次对应MAC或连接失败,回退使用扫码中的MAC...\n"); + } + } + // 若未识别到批次号,给出提示 + if (extract_batch(scan, batch, sizeof(batch)) != 0) { + fprintf(stderr, "未识别到批次号,回退使用扫码中的MAC...\n"); + } + char mac_colon[32] = {0}; + if (extract_mac(scan, mac_colon, sizeof(mac_colon)) != 0) { + fprintf(stderr, "Failed to extract MAC from input: %s\n", scan); + return 2; + } + + if (update_hwaddress_in_interfaces(interfaces_path, mac_colon) != 0) { + return 3; + } + + return 0; +} + +static volatile int led_running = 0; +static pthread_t led_thread; + +static void gpio_init_yellow(void) { + int fd = open("/sys/class/gpio/export", O_WRONLY); + if (fd >= 0) { + write(fd, "114", 3); + close(fd); + } + fd = open("/sys/class/gpio/gpio114/direction", O_WRONLY); + if (fd >= 0) { + write(fd, "out", 3); + close(fd); + } +} + +static void* led_blink_thread(void* arg) { + int fd = open("/sys/class/gpio/gpio114/value", O_WRONLY); + if (fd < 0) return NULL; + while (led_running) { + write(fd, "1", 1); + usleep(200000); + write(fd, "0", 1); + usleep(200000); + } + // ensure off + write(fd, "0", 1); + close(fd); + return NULL; +} + +static void start_yellow_blink(void) { + if (led_running) return; + gpio_init_yellow(); + led_running = 1; + pthread_create(&led_thread, NULL, led_blink_thread, NULL); +} + +static void stop_yellow_blink(void) { + if (!led_running) return; + led_running = 0; + // 等待线程退出 + pthread_join(led_thread, NULL); +} + +static void set_yellow_on(void) { + // 确保不再闪烁 + stop_yellow_blink(); + // 初始化GPIO并置为高电平 + gpio_init_yellow(); + int fd = open("/sys/class/gpio/gpio114/value", O_WRONLY); + if (fd < 0) return; + write(fd, "1", 1); + close(fd); +} \ No newline at end of file diff --git a/update_mac_pdd b/update_mac_pdd new file mode 100755 index 0000000..bdcc4b1 Binary files /dev/null and b/update_mac_pdd differ