Установка FreeBSD. Часть 1

В этой заметке мы рассмотрим установку FreeBSD на новую машину (в том смысле, что не содержащую другую операционную систему) с дистрибутивного DVD. Устаноновка и постинсталляционное конфигурирование FreeBSD осуществляются программмой sysinstall. В функции sysinstall входят подготовка дисковых разделов, установка базовых компонентов системы и дополнительных пакетов, управление пользователями, установка языковой поддержки и множество дополнительных настроек.

Программа sysinstall запускается автоматически с установочного DVD после перезагрузки машины (после включения соответствующей опции в BIOS Setup). После выбора страны - Russian Federation (Российская Федерация)

и раскладки клавиатуры - Russia KOI8-R

попадаем в главное меню программы sysinstall - Sysinstall Main Menu:

Sysinstall - программа текстового режима с интерфейсом, оформленным псевдографикой. Выбор опций осуществляется в меню исключительно стрелками перемещения курсора, клавишами [Tab], [Spacebar] и [Enter].

Первым пунктом в нем (Usage) следует краткая справка по использованию программы. Следующие три пункта - Standard (Стандартная), Express (Быстрая) и Custom (Выборочная) - предлагают три типа установки: стандартный, быстрый и заказной. Первые два варианта мы рассматривать не будем - зачем устанавливать программы, которые нам не нужны. Поэтому выбираем третий тип установки.

При выборе его вызывается меню Choose Custom Installation Options позволяющее выбрать опции заказной установки:

  • Options (Опции)
  • Partition (Создание слайсов)
  • Label (Создание разделов)
  • Distributions (Основные компоненты)
  • Media (Источники установки)
  • Commit (Подтверждение)

Пункт Options (Опции) вызывает редактор опций - таких как сетевые параметры, тип терминала, редактор по умолчанию и т.п. Пропустим пока этот пункт.

При выборе следующего пункта - Partition (Создание слайсов), на экране появится окно редактора слайсов:

Поскольку на компьютере не предполагается использование других ОС, можно ограничиться одним слайсом для FreeBSD. Так что выбираем опцию A = Entire disk (Полный диск) и выходим из редактора - Q = Finish.

После этого нам будет предложено установить загрузчик FreeBSD:

  • BootMgr - установить менеджер загрузок FreeBSD
  • Standard - стандартная MBR
  • None - не трогать MBR

Поскольку на машине будет только одна операционная система, нет необходимости устанавливать менеджер загрузки - поэтому выбираем второй пункт.

Теперь нам нужен пункт Label (Создание разделов)

чтобы разбить слайс FreeBSD на разделы:

Выбираем A = Auto Defaults и Q = Finish для выхода.

Пункт Distributions (Основные компоненты)

отвечает за выбор базовых компонентов установки:

Все они сгруппированы в некие предопределенные наборы - для разработчиков и пользователей разного профиля, минимально необходимый набор. Есть возможность сделать выбор вручную или установить весь штатный комплект. Выбираем пункт Minimal с помощью клавишы [SpaseBar], с помощью [Tab] выбираем [OK], жмем [Enter].

В пункте Media (Источники установки)

выбираем источник инсталляции - CD/DVD, FTP в активном или пассивном режиме и т.п. При запуске sysinstall с инсталляционного DVD необходимости в выборе первого пункта нет - он включен по умолчанию.

Смысл этого пункта в возможности смены источника в процессе установки.

Наконец, пунктом Commit (Подтверждение) завершается процесс базовой установки:

Он запрашивает подтверждение на выполнение всех заказанных опций и действий. В том числе - и на создание дисковых разделов и файловых систем на них. До того, как такое подтверждение последует, с диском физически ничего не происходит.

После подтверждения диск разбивается на разделы, на них создаются файловые системы и начинается установка выбранных компонентов:

После завершения установки мы увидим сообщение с предложением выполнить постинсталляционное конфигурирование системы:

Мы сделаем это немного позже - после перезагрузки, поэтому выбираем [No] и попадаем опять в Choose Custom Installation Options

Выбираем Exit, чтобы вернуться в главное меню программы sysinstall. Теперь можно выйти из программы установки, выбрав Exit Install (Завершение установки)

что автоматически влечет за собой перезагрузку системы

Не забудьте вынуть DVD из привода или изменить установки BIOS!

После того, как система загрузится, будет предложено ввести логин пользователя - root:

На сегодня, пожалуй, все. Чтобы завершить работу и выключить питание компьютера, набираем в командной строке:

shutdown -p now

Продолжение следует…

Скачивание файлов по временным ссылкам

Наверное каждому приходилось сталкиваться с временными ссылками при скачивании фильмов, музыки, программ и т.п. Зачем это делается? Да чтобы другие сайты не размещали ссылки на файлы, которые расположены на нашем сайте. Давайте посмотрим, как написать скрипт, который будет генерить временные ссылки.

В качестве хранения информации о файлах и временных ссылках, будем использовать БД. Таблица files хранит информацию о файлах:

CREATE TABLE `files` (
  `id` INT(10) PRIMARY KEY,
  `title` VARCHAR(255) NOT NULL DEFAULT '',
  `description` TEXT NOT NULL DEFAULT '',
  `filename` VARCHAR(64) NOT NULL DEFAULT '',
  `mimetype` VARCHAR(8) NOT NULL DEFAULT ''
) ENGINE=INNODB DEFAULT CHARSET=cp1251;

Здесь

  • id - уникальный ID файла
  • title - название файла, например, “Текстовой редактор NotePad++
  • description - описание файла, например, “Бесплатный редактор текстовых файлов (замена стандартного Блокнота) с поддержкой синтаксиса большого количества языков программирования, ориентирован для работы в операционной системе MS Windows
  • filename - имя файла для скачивания, например, NotePadPP.zip
  • mimetype - MIME-тип файла

Таблица downloads хранит информацию о временных ссылках:

CREATE TABLE `downloads` (
  `file_id` INT(10) NOT NULL DEFAULT 0,
  `uniq_id` VARCHAR(32) NOT NULL DEFAULT '',
  `puttime` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00'
) ENGINE=INNODB DEFAULT CHARSET=cp1251;

Здесь

  • file_id – уникальный ID файла
  • uniq_id – временная ссылка
  • puttime - время создания ссылки

Файлы для скачивания расположены в директории DOCUMENT_ROOT/download/files/. Эта директория должна быть защищена с помощью .htaccess:

Order Allow,Deny
Deny from All

Скрипт, который будет выполнять всю работу - выводить список файлов, генерить временные ссылки, и отдавать файлы на скачивание - DOCUMENT_ROOT/download/index.php

<?php

// Соединяемся с сервером БД
mysql_connect ( 'localhost', 'root', '' );
mysql_query( 'SET NAMES cp1251' );
mysql_select_db ( 'downloads' );

// удаляем устаревшие записи в таблице БД downloads
$query = 'DELETE FROM downloads WHERE puttime < (NOW() - INTERVAL 12 HOUR)';
mysql_query( $query );

$actions = array( 'fileslist', 'getlink', 'download' );

$action = 'fileslist';
if( isset( $_GET['action'] ) and in_array( $_GET['action'], $actions ) ) $action = $_GET['action'];

switch( $action ) {
  case 'fileslist':      // список файлов для скачивания
    fileslist(); break;
  case 'getlink':        // создаем временную ссылку
    getlink(); break;
  case 'download':       // отдаем файл на скачивание
    download()break;
}

function fileslist() {

  echo '<h3>Файлы для скачивания</h3>'."\n";
  $query = 'SELECT id, title, description, mimetype FROM `files` WHERE 1 ORDER BY title';
  $res = mysql_query( $query );

  echo '<table border="1">'."\n";
  echo '<tr><th>№</th><th>Наименование</th><th>Описание</th><th>Тип</th><th>Скачать</th></tr>'."\n";
  $i = 1;
  while( $file = mysql_fetch_array( $res ) ) {
    echo '<tr>';
    echo '<td>'.$i.'</td>';
    echo '<td>'.$file['title'].'</td>';
    echo '<td>'.$file['description'].'</td>';
    echo '<td>'.$file['mimetype'].'</td>';
    echo '<td><a href="'.$_SERVER['PHP_SELF'].'?action=getlink&id='.$file['id'].'">Скачать</a></td>';
    echo '</tr>'."\n";
    $i++;
  }
  echo '</table>'."\n";
}

function getlink() {

  // если не передан уникальный ID файла - значит пользователь попал сюда по ошибке
  if( !isset( $_GET['id'] ) ) {
    header( 'Location: '.$_SERVER['PHP_SELF'].'?action=fileslist' );
    die();
  }
  $id = (int)$_GET['id'];

  // прежде чем генерить временную ссылку, проверяем, что есть такая запись в таблице БД
  $query = 'SELECT 1 FROM `files` WHERE id='.$id;
  $res = mysql_query( $query );
  if( mysql_num_rows( $res ) == 0 ) {
    header ( 'HTTP/1.1 404 Not Found' );
    die()
  }
 
  $uniq_id = md5( uniqid(rand(), 1) );
  $query = "INSERT INTO downloads (file_id, uniq_id, puttime)
            VALUES ("
.$id.", '".$uniq_id."', NOW())";
  mysql_query( $query );
 
  $link = $_SERVER['PHP_SELF'].'?action=download&id='.$id.'&code='.$uniq_id;
  echo '<p>Для загрузки файла перейдите по <a href="'.$link.'">этой ссылке</a>. ';
  echo 'Ссылка действительна в течение 12 часов.</p>'."\n";

}

function download() {
 
  // если не передан уникальный ID файла - значит пользователь попал сюда по ошибке
  if( !isset( $_GET['id'] ) ) {
    header( 'Location: '.$_SERVER['PHP_SELF'].'?action=fileslist' );
    die();
  }
  $id = (int)$_GET['id'];
 
  if( !isset( $_GET['code'] ) )  {
    header( 'Location: '.$_SERVER['PHP_SELF'].'?action=fileslist' );
    die();
  }
 
  if( !preg_match( '#[a-f0-9]{32}#', $_GET['code'] ) )  {
    header ( 'HTTP/1.1 404 Not Found' );
    die();
  }
 
  $query = "SELECT 1 FROM downloads WHERE file_id=".$id."
            AND uniq_id='"
.$_GET['code']."' AND puttime > (NOW() - INTERVAL 12 HOUR)";
  $res = mysql_query( $query );
  if( mysql_num_rows( $res ) == 0 ) {
    header ( 'HTTP/1.1 404 Not Found' );
    die()
  }
 
  $query = 'SELECT filename, mimetype FROM `files` WHERE id='.$id;
  $res = mysql_query( $query );
  if( mysql_num_rows( $res ) == 0 ) {
    header ( 'HTTP/1.1 404 Not Found' );
    die()
  }
  list( $filename, $mimetype ) = mysql_fetch_row( $res );
 
  // если файла нет
  if( !file_exists( './files/'.$filename ) ) {
    header ( 'HTTP/1.1 404 Not Found' );
    die();
  }
 
  // получаем размер файла
  $fsize = filesize( './files/'.$filename );
  // дата модификации файла для кеширования
  $ftime = date( 'D, d M Y H:i:s T', filemtime( './files/'.$filename ) );
  // смещение от начала файла
  $range = 0;
 
  // пробуем открыть
  $handle = @fopen( './files/'.$filename, 'rb' );

  // если не удалось
  if( !$handle ){
    header ( 'HTTP/1.1 404 Not Found' );
    die();
  }
 
  // если запрашивающий агент поддерживает докачку
  if( $_SERVER['HTTP_RANGE'] ) {
    $range = $_SERVER['HTTP_RANGE'];
    $range = str_replace( 'bytes=', '', $range );
    $range = str_replace( '-', '', $range );
    // смещаемся по файлу на нужное смещение
    if ( $range ) fseek( $handle, $range );
  }
 
  // если есть смещение
  if( $range ) {
    header( 'HTTP/1.1 206 Partial Content' );
  } else {
    header( 'HTTP/1.1 200 OK' );
  }
 
  header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
  header( 'Last-Modified: '.$ftime );
  header( 'Content-Length: '.($fsize-$range) );
  header( 'Accept-Ranges: bytes' );
  header( 'Content-Range: bytes '.$range.'-'.($fsize - 1).'/'.$fsize );

  switch( $mimetype ) {
    case 'pdf' : $ctype = 'application/pdf'; break;
    case 'zip' : $ctype = 'application/zip'; break;
    case 'doc' : $ctype = 'application/msword'; break;
    case 'xls' : $ctype = 'application/vnd.ms-excel'; break;
    case 'gif' : $ctype = 'image/gif'; break;
    case 'png' : $ctype = 'image/png'; break;
    case 'jpeg':
    case 'jpg' : $ctype = 'image/jpg'; break;
    case 'mp3' : $ctype = 'audio/mpeg'; break;
    case 'wav' : $ctype = 'audio/x-wav'; break;
    case 'mpeg':
    case 'mpg' :
    case 'mpe' : $ctype = 'video/mpeg'; break;
    case 'mov' : $ctype = 'video/quicktime'; break;
    case 'avi' : $ctype = 'video/x-msvideo'; break;
    default    : $ctype = 'application/octet-stream';
  }
 
  header( 'Content-Type: '.$ctype );
 
  readfile( './files/'.$filename );
 
  fclose( $handle );
 
}
 
?>

Обработка значения NULL

В синтаксисе SQL значение NULL занимает особое место - оно соответствует понятию “ничего”. Значение NULL не равно пустой строке или нулю, и эта особенность часто порождает ошибки. Рассмотрим несколько примеров:

SELECT NULL=FALSE;
SELECT NULL='';
SELECT NULL=0;
SELECT NULL=NULL;

NULL значения

Результатом всех запросов будет NULL. Это может выглядеть странно, но значение NULL не является значением в полном смысле слова: по определению оно означает отсутствие значения и не принадлежит ни одному типу данных. Поэтому NULL не равно ни логическому значению FALSE, ни пустой строке, ни нулю. При сравнении NULL с любым значением будет получен результат NULL, а не FALSE и не 0. Более того, NULL не равно NULL, и это иллюстрирует последний из приведенных запросов.

Если вы работаете со столбцом, в котором могут присутствовать значения NULL, необходимо учитывать следующее: нельзя использовать запросы вида

SELECT * FROM some_table WHERE some_column = NULL;
SELECT * FROM some_table WHERE some_column != NULL;

Такие запросы не вернут ни одной строки. Используйте запросы вида

SELECT * FROM some_table WHERE some_column IS NULL;
SELECT * FROM some_table WHERE some_column IS NOT NULL;