POSA_LEAKSMS/firmware/common/applog.c
유창욱 90f121e14c chore: import codebase with security hardening
SHT30 온습도 모니터링 시스템 전체 소스(서버 PHP, STM32 펌웨어, SQL, 테스트).
전체 코드리뷰에서 도출된 보안 하드닝 10건 반영:
- 요청 서명 HMAC-SHA256 전환(펌웨어 sig.c/서버 config.php/호스트 패리티 동시)
- 재전송 방어 + 기본 API_KEY fail-closed + 디바이스 문자열 정제(api/sensor_data.php)
- 오프라인 SMS 중복 발송 경합 제거(cron_heartbeat.php, 원자적 선점)
- CSV 수식 주입 방지(monthly_report.php), 감사로그 회전 락(retention_cleanup.php)
- 브루트포스 카운터 원자화(login.php), 예시 TOTP 비밀키 무효화, 마이그레이션 멱등화

_backup/(하드코딩 실 비밀값 포함)·config.local.php·런타임 상태는 .gitignore 제외.
2026-06-20 09:37:40 +09:00

176 lines
6.4 KiB
C

/* =============================================================================
* applog.c - UART 디버그 로그 (RPi setup_logger() 대응)
*
* USART3(PD8 TX / PD9 RX, APP_LOG_UART_BAUD) 로 printf 스타일
* 라인 로그를 출력한다. 라인은 다음 형식으로 찍힌다:
*
* [ 12345 ms] INFO 메시지...
*
* 직렬화:
* FreeRTOS 스케줄러 동작 중이면 뮤텍스로 라인 단위 직렬화한다.
* 부팅 초기(스케줄러 시작 전, 또는 ISR 문맥)에는 뮤텍스 없이 그대로 출력한다.
*
* USART3 HAL 핸들과 MspInit(핀/클럭) 은 이 파일이 소유한다.
* ===========================================================================*/
#include "applog.h"
#include "app_config.h"
#include "stm32f4xx_hal.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
/* ── 설정 상수 ───────────────────────────────────────────────────────────── */
/* 한 줄 로그 최대 길이(접두부 포함). 스택 버퍼 크기. */
#define LOG_LINE_MAX 160
/* HAL_UART_Transmit 블로킹 타임아웃(ms). */
#define LOG_TX_TIMEOUT_MS 100u
/* 뮤텍스 획득 대기(ms). 초과 시 직렬화 포기하고 그대로 출력(로그는 best-effort). */
#define LOG_MUTEX_WAIT_MS 50u
/* ── USART3 HAL 핸들 (이 모듈 소유) ───────────────────────────────────────── */
static UART_HandleTypeDef huart_log;
/* ── 직렬화용 뮤텍스 + 초기화 플래그 ─────────────────────────────────────── */
static SemaphoreHandle_t s_logMutex = NULL;
static volatile int s_inited = 0;
/* =============================================================================
* USART3 MspInit — PD8(TX)/PD9(RX), AF7, 클럭 게이팅
* HAL_UART_Init() 내부에서 HAL_UART_MspInit() 약함수 형태로 호출된다.
* (여러 UART 를 쓰는 프로젝트라면 instance 분기. 여기선 USART3 만 처리.)
* ===========================================================================*/
void HAL_UART_MspInit(UART_HandleTypeDef *uartHandle)
{
GPIO_InitTypeDef g = {0};
if (uartHandle->Instance != USART3) {
return; /* 다른 UART 는 해당 모듈이 처리 */
}
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_USART3_CLK_ENABLE();
/* PD8=USART3_TX, PD9=USART3_RX, AF7.
* (PA2/PA3 는 LAN8720 RMII 의 ETH_MDIO/미사용과 충돌하므로 사용 불가 →
* 로그 UART 는 USART3 PD8/PD9 로 배치한다. HARDWARE.md 핀맵 참조.) */
g.Pin = GPIO_PIN_8 | GPIO_PIN_9;
g.Mode = GPIO_MODE_AF_PP;
g.Pull = GPIO_PULLUP;
g.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
g.Alternate = GPIO_AF7_USART3;
HAL_GPIO_Init(GPIOD, &g);
}
/* =============================================================================
* applog_init — USART3 초기화 + 로그 뮤텍스 생성
* ===========================================================================*/
void applog_init(void)
{
huart_log.Instance = USART3;
huart_log.Init.BaudRate = APP_LOG_UART_BAUD;
huart_log.Init.WordLength = UART_WORDLENGTH_8B;
huart_log.Init.StopBits = UART_STOPBITS_1;
huart_log.Init.Parity = UART_PARITY_NONE;
huart_log.Init.Mode = UART_MODE_TX_RX;
huart_log.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart_log.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart_log) != HAL_OK) {
/* UART 를 못 켜면 로그는 무음 처리(디바이스 동작 자체는 계속). */
s_inited = 0;
return;
}
/* 뮤텍스 생성 시도. 실패해도(메모리 부족 등) 로그는 비직렬화로 동작. */
s_logMutex = xSemaphoreCreateMutex();
s_inited = 1;
}
/* ── 스케줄러가 돌고 있고 ISR 문맥이 아닐 때만 뮤텍스를 쓴다 ──────────────── */
static int log_can_use_mutex(void)
{
if (s_logMutex == NULL) {
return 0;
}
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
return 0; /* 스케줄러 시작 전: 뮤텍스 블로킹 불가 */
}
if (__get_IPSR() != 0u) {
return 0; /* ISR 문맥: 블로킹 뮤텍스 호출 금지 */
}
return 1;
}
/* ── 원시 바이트 전송(블로킹) ────────────────────────────────────────────── */
static void log_tx(const char *buf, uint16_t len)
{
if (len == 0u) {
return;
}
(void)HAL_UART_Transmit(&huart_log, (uint8_t *)buf, len, LOG_TX_TIMEOUT_MS);
}
/* =============================================================================
* applog — printf 스타일 라인 로그
* 형식: "[%8lu ms] %-5s <msg>\r\n"
* ===========================================================================*/
void applog(const char *level, const char *fmt, ...)
{
char line[LOG_LINE_MAX];
int n;
int pos = 0;
va_list ap;
int locked = 0;
if (!s_inited) {
return; /* UART 미초기화: 무음 */
}
if (level == NULL) {
level = "?";
}
/* 업타임 스탬프(ms). 스케줄러 전/후 모두 HAL_GetTick() 으로 일관 — TIM6 기반. */
n = snprintf(line, sizeof(line), "[%8lu ms] %-5s ",
(unsigned long)HAL_GetTick(), level);
if (n < 0) {
return;
}
pos = (n < (int)sizeof(line)) ? n : (int)sizeof(line) - 1;
/* 사용자 메시지 */
va_start(ap, fmt);
n = vsnprintf(line + pos, sizeof(line) - (size_t)pos, fmt, ap);
va_end(ap);
if (n > 0) {
pos += n;
if (pos > (int)sizeof(line) - 1) {
pos = (int)sizeof(line) - 1; /* vsnprintf 가 잘라낸 경우 */
}
}
/* 줄바꿈(\r\n). 공간이 모자라면 마지막 2바이트를 덮어쓴다. */
if (pos > (int)sizeof(line) - 3) {
pos = (int)sizeof(line) - 3;
}
line[pos++] = '\r';
line[pos++] = '\n';
/* 직렬화 후 전송 */
if (log_can_use_mutex()) {
if (xSemaphoreTake(s_logMutex, pdMS_TO_TICKS(LOG_MUTEX_WAIT_MS)) == pdTRUE) {
locked = 1;
}
}
log_tx(line, (uint16_t)pos);
if (locked) {
(void)xSemaphoreGive(s_logMutex);
}
}