分类 技术杂记 下的文章

#!/bin/sh

# ==============================================================================
# NOR Flash Lifetime Stress Test for Embedded Linux (BusyBox)
#
# Version: 1.2
#
# Description:
# Performs a continuous write/read/verify cycle on a NOR flash MTD partition.
# Creates a status file in /tmp for monitoring during long-running tests.
#
# WARNING: THIS SCRIPT IS DESTRUCTIVE. IT WILL ERASE ALL DATA ON THE
#          SPECIFIED FLASH DEVICE. USE WITH EXTREME CAUTION.
#
# How to monitor:
# In another shell, run: cat /tmp/nor_stress_test_status.log
# ==============================================================================

# --- CONFIGURATION ---
FLASH_DEVICE="/dev/mtdblock5"
TEST_FILE_SIZE_KB=64
TEMP_DIR="/tmp"
# --- END OF CONFIGURATION ---


# --- SCRIPT LOGIC ---
SOURCE_FILE="${TEMP_DIR}/stress_source.dat"
READBACK_FILE="${TEMP_DIR}/stress_readback.dat"
SOURCE_MD5_FILE="${TEMP_DIR}/source.md5"
READBACK_MD5_FILE="${TEMP_DIR}/readback.md5"
STATUS_FILE="${TEMP_DIR}/nor_stress_test_status.log"

START_TIME=$(date)
passed_cycles=0
cycle_count=1

# Function to write the current status to a log file
update_status() {
  current_status_msg="$1"
  # Overwrite the status file with the latest info
  {
    printf "--- NOR Flash Stress Test Status ---\n"
    printf "Test Start Time:     %s\n" "${START_TIME}"
    printf "Last Update:         %s\n" "$(date)"
    printf "Total Cycles Passed: %d\n" "${passed_cycles}"
    printf "Current Cycle:       %d\n" "${cycle_count}"
    printf "Current Status:      %s\n" "${current_status_msg}"
  } > "${STATUS_FILE}"
}

# Function to clean up temporary files on exit
cleanup() {
  printf "\nCleaning up temporary files...\n"
  rm -f "${SOURCE_FILE}" "${READBACK_FILE}" "${SOURCE_MD5_FILE}" "${READBACK_MD5_FILE}"
  printf "Cleanup complete. Exiting.\n"
}

trap cleanup INT TERM EXIT

# --- Initial Sanity Checks ---
printf "============================================================\n"
printf "       NOR Flash Stress Test Script (v1.2)\n"
printf "============================================================\n"
printf "Target Device:     %s\n" "${FLASH_DEVICE}"
printf "Test Data Size:    %d KB\n" "${TEST_FILE_SIZE_KB}"
printf "Status Log File:   %s\n" "${STATUS_FILE}"
printf "------------------------------------------------------------\n"
printf "!!! WARNING: THIS WILL DESTROY ALL DATA ON %s !!!\n" "${FLASH_DEVICE}"
printf "------------------------------------------------------------\n"
printf "Type 'YES' to continue: "
read -r confirmation
if [ "${confirmation}" != "YES" ]; then
  printf "Confirmation not received. Aborting.\n"
  exit 1
fi

if [ ! -b "${FLASH_DEVICE}" ]; then
  printf "Warning: Device %s not found or is not a character device.\n" "${FLASH_DEVICE}"
fi

printf "Starting stress test. Press Ctrl+C to stop.\n"
printf "You can monitor progress by running 'cat %s'\n" "${STATUS_FILE}"

# --- Main Test Loop ---
while true; do
  printf "\n----- Cycle %d -----\n" "${cycle_count}"

  # 1. Create source file
  update_status "Creating random source file"
  printf "[1/5] Creating %d KB random source file...\n" "${TEST_FILE_SIZE_KB}"
  dd if=/dev/urandom of="${SOURCE_FILE}" bs=1k count="${TEST_FILE_SIZE_KB}" 2>/dev/null
  if [ $? -ne 0 ]; then
    update_status "ERROR: Failed to create source file. Test stopped."
    printf "Error: Failed to create source file. Aborting.\n"
    exit 1
  fi

  # 2. Calculate source MD5
  update_status "Calculating source MD5 hash"
  printf "[2/5] Calculating source file MD5 hash...\n"
  md5sum "${SOURCE_FILE}" > "${SOURCE_MD5_FILE}"
  read -r source_md5 _ < "${SOURCE_MD5_FILE}"
  printf "      Source MD5: %s\n" "${source_md5}"

  # 3. Write to flash
  update_status "Writing data to ${FLASH_DEVICE}"
  printf "[3/5] Writing data to %s...\n" "${FLASH_DEVICE}"
  dd if="${SOURCE_FILE}" of="${FLASH_DEVICE}" bs=1k 2>/dev/null
  if [ $? -ne 0 ]; then
    update_status "ERROR: Failed to write to flash. Test stopped."
    printf "Error: Failed to write to %s. Aborting.\n" "${FLASH_DEVICE}"
    exit 1
  fi

  # 4. Read back from flash
  update_status "Reading data back from ${FLASH_DEVICE}"
  printf "[4/5] Reading data back from %s...\n" "${FLASH_DEVICE}"
  dd if="${FLASH_DEVICE}" of="${READBACK_FILE}" bs=1k count="${TEST_FILE_SIZE_KB}" 2>/dev/null
  if [ $? -ne 0 ]; then
    update_status "ERROR: Failed to read from flash. Test stopped."
    printf "Error: Failed to read from %s. Aborting.\n" "${FLASH_DEVICE}"
    exit 1
  fi

  # 5. Verify data
  update_status "Verifying data integrity"
  printf "[5/5] Verifying data integrity...\n"
  md5sum "${READBACK_FILE}" > "${READBACK_MD5_FILE}"
  read -r readback_md5 _ < "${READBACK_MD5_FILE}"
  printf "      Readback MD5: %s\n" "${readback_md5}"

  if [ "${source_md5}" = "${readback_md5}" ]; then
    passed_cycles=$((passed_cycles + 1))
    printf "      SUCCESS: Hashes match. Cycle %d passed.\n" "${cycle_count}"
    update_status "Cycle passed. Waiting for next cycle."
  else
    failure_msg="FAILURE: HASH MISMATCH on cycle ${cycle_count}"
    update_status "${failure_msg}"
    printf "      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
    printf "      !!! %s !!!\n" "${failure_msg}"
    printf "      !!! Source MD5:   %s\n" "${source_md5}"
    printf "      !!! Readback MD5: %s\n" "${readback_md5}"
    printf "      !!! Flash may be failing. Aborting test. !!!\n"
    printf "      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
    mv "${SOURCE_FILE}" "${TEMP_DIR}/FAILED_source_cycle_${cycle_count}.dat"
    mv "${READBACK_FILE}" "${TEMP_DIR}/FAILED_readback_cycle_${cycle_count}.dat"
    exit 1
  fi

  cycle_count=$((cycle_count + 1))
done

实现指定目录下保留最新N个文件的Shell脚本:

#!/bin/bash

# 参数检查
if [ $# -ne 2 ]; then
    echo "Usage: $0 <directory> <number_of_files_to_keep>"
    exit 1
fi

directory="$1"
n="$2"

# 验证目录是否存在
if [ ! -d "$directory" ]; then
    echo "Error: Directory '$directory' does not exist."
    exit 1
fi

# 验证保留数量是否为合法数字
if ! [[ "$n" =~ ^[0-9]+$ ]]; then
    echo "Error: Invalid number format. Please enter a positive integer."
    exit 1
fi

# 安全删除旧文件的命令链
find "$directory" -maxdepth 1 -type f -printf "%T@ %p\0" | \
  sort -z -k1nr | \
  cut -z -d' ' -f2- | \
  tail -z -n +$(($n + 1)) | \
  xargs -0 -r rm -f -- 

echo "Success: Kept latest $n files in '$directory'."

使用示例:

# 保留/var/log目录下最新的5个文件
sudo ./keep_latest.sh /var/log 5

Serv00服务器经常出现删除cron现象,通过外部SSH访问重设比较麻烦,同一个IP对多服务器SSH访问,还可能会被批量查封。
可以借用web进行应用保活,简单又安全,方法如下:

  • 替换public_html目录下的默认主页
    删除/home/${YOURNAME}/domains/${YOURNAME}.serv00.net/public_html目录下的index.html,增加:
    index.php:

    <?php
    
    // 引入配置文件
    $config = include 'config.php';
    
    // 日志文件路径
    $logFile = 'log/access.log';
    
    // Nginx 默认页面 HTML 内容
    $nginxDefaultHtml = <<<HTML
    <!DOCTYPE html>
    <html>
    <head>
      <title>Welcome to nginx!</title>
      <style>
          body {
              width: 35em;
              margin: 0 auto;
              font-family: Tahoma, Verdana, Arial, sans-serif;
          }
      </style>
    </head>
    <body>
    <h1>Welcome to nginx!</h1>
    <p>If you see this page, the nginx web server is successfully installed and
    working. Further configuration is required.</p>
    
    <p>For online documentation and support please refer to
    <a href="http://nginx.org/">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="http://nginx.com/">nginx.com</a>.</p>
    
    <p><em>Thank you for using nginx.</em></p>
    </body>
    </html>
    HTML;
    
    // 确保 log 目录存在
    if (!is_dir('log')) {
      mkdir('log', 0777, true); // 创建目录,允许所有用户读写执行
    }
    
    // 获取请求路径 (从 GET 参数中获取)
    $requestPath = $_GET['path'] ?? '';
    
    // 记录访问日志
    $logMessage = date('Y-m-d H:i:s') . " - Access: " . ($requestPath ? '?path=' . $requestPath : 'Direct Access') . "\n";
    file_put_contents($logFile, $logMessage, FILE_APPEND);
    
    // 移除多余的斜杠
    $requestPath = trim($requestPath, '/');
    
    // 添加前导斜杠,使其与配置文件中的路由匹配
    if ($requestPath !== '') {
      $requestPath = '/' . $requestPath;
    }
    
    // 查找匹配的配置项
    $action = null;
    foreach ($config['routes'] as $route => $actionConfig) {
      if ($route === $requestPath) {
          $action = $actionConfig;
          break;
      }
    }
    
    if ($action) {
      // 执行对应操作
      switch ($action['type']) {
          case 'command':
              $command = $action['command'];
              $logMessage = date('Y-m-d H:i:s') . " - Executing command: " . $command . "\n";
              file_put_contents($logFile, $logMessage, FILE_APPEND);
    
              // 执行命令并获取输出
              $output = shell_exec($command . ' 2>&1'); //  2>&1  将标准错误重定向到标准输出
    
              $logMessage = date('Y-m-d H:i:s') . " - Command output: " . $output . "\n";
              file_put_contents($logFile, $logMessage, FILE_APPEND);
    
              echo "<pre>" . htmlspecialchars($output) . "</pre>"; // 使用 <pre> 标签保持格式,并进行HTML转义
              break;
    
          case 'static':
              $filePath = $action['file'];
    
              if (is_dir($filePath)) {
                  // 目录托管
                  $requestedFile = realpath($filePath . '/' . ltrim($requestPath, '/'));  // 确保在目录内
                  $logMessage = date('Y-m-d H:i:s') . " - Serving static directory: " . $filePath . ", Requested file: " . $requestedFile . "\n";
                  file_put_contents($logFile, $logMessage, FILE_APPEND);
    
                  if ($requestedFile && strpos($requestedFile, realpath($filePath)) === 0 && file_exists($requestedFile) && is_readable($requestedFile)) {  // 安全检查
                      // 根据文件类型设置 Content-Type
                      $fileExtension = pathinfo($requestedFile, PATHINFO_EXTENSION);
                      $contentType = getContentType($fileExtension);  // 使用函数获取 Content-Type
                      header('Content-Type: ' . $contentType);
                      readfile($requestedFile);
                  } else {
                      header("HTTP/1.0 404 Not Found");
                      echo "<h1>404 Not Found</h1>";
                      $logMessage = date('Y-m-d H:i:s') . " - Static file not found: " . $filePath . '/' . ltrim($requestPath, '/') . "\n";
                      file_put_contents($logFile, $logMessage, FILE_APPEND);
                  }
              } elseif (file_exists($filePath) && is_readable($filePath)) {
                  // 单个文件
                  $logMessage = date('Y-m-d H:i:s') . " - Serving static file: " . $filePath . "\n";
                  file_put_contents($logFile, $logMessage, FILE_APPEND);
    
                  // 根据文件类型设置 Content-Type
                  $fileExtension = pathinfo($filePath, PATHINFO_EXTENSION);
                  $contentType = getContentType($fileExtension);  // 使用函数获取 Content-Type
                  header('Content-Type: ' . $contentType);
    
                  readfile($filePath);
              } else {
                  header("HTTP/1.0 404 Not Found");
                  echo "<h1>404 Not Found</h1>";
                  $logMessage = date('Y-m-d H:i:s') . " - Static file not found: " . $filePath . "\n";
                  file_put_contents($logFile, $logMessage, FILE_APPEND);
              }
              break;
    
          default:
              header("HTTP/1.0 500 Internal Server Error");
              echo "<h1>500 Internal Server Error</h1><p>Invalid action type.</p>";
              $logMessage = date('Y-m-d H:i:s') . " - Invalid action type: " . $action['type'] . "\n";
              file_put_contents($logFile, $logMessage, FILE_APPEND);
              break;
      }
    } else {
      // 未找到匹配的路由,返回 Nginx 默认页面
      header('Content-Type: text/html');
      echo $nginxDefaultHtml;
      $logMessage = date('Y-m-d H:i:s') . " - Route not found, serving Nginx default page.\n";
      file_put_contents($logFile, $logMessage, FILE_APPEND);
    }
    
    /**
     * 根据文件扩展名获取 Content-Type
     * @param string $extension
     * @return string
     */
    function getContentType(string $extension): string {
      $mimeTypes = [
          'html' => 'text/html',
          'css' => 'text/css',
          'js' => 'text/javascript',
          'json' => 'application/json',
          'xml' => 'application/xml',
          'txt' => 'text/plain',
          'jpg' => 'image/jpeg',
          'jpeg' => 'image/jpeg',
          'png' => 'image/png',
          'gif' => 'image/gif',
          'svg' => 'image/svg+xml',
          // 添加更多 mime 类型
      ];
    
      return $mimeTypes[$extension] ?? 'application/octet-stream'; // 默认
    }
    
    ?>

config.php:

<?php

return [
    'routes' => [
        '/status' => [
            'type' => 'command',
            'command' => 'uptime'
        ],
        '/diskspace' => [
            'type' => 'command',
            'command' => 'df -h'
        ],
        '/cron' => [
            'type' => 'command',
            'command' => 'bash ./your_check_cron_shell.sh'
        ],
        '/installcron' => [
            'type' => 'command',
            'command' => '(crontab -l 2>/dev/null; echo "00 08 * * 6 ${your_command} > /dev/null 2>&1") | crontab -'
        ],
        '/back.html' => [
            'type' => 'static',
            'file' => 'your.html'  // 单个文件
        ]
    ]
];

按需修改上面的配置文件对应的脚本或命令,尽情发挥。

  • WEB远程调用示例

    https://${YOURNAME}.serv00.net/?path=cron

网上看到的方法,收集一下。
所谓别名就是同一个邮箱,但是地址形式可以多种,以方便多注册……

比如 [email protected] 这个邮箱:

所有邮件接受到[email protected] 这个邮箱