921 заметка с тегом

программирование

Позднее Ctrl + ↑

Функция sleep и сигналы

Век живи, век учись. Вот уж не знал, что при получении сигнала программой на ПХП, моментально прерывается выполнение функции sleep (и родственных ей time_nanosleep и usleep):

$ php -r 'pcntl_signal(SIGUSR1, "_"); var_dump(sleep(PHP_INT_MAX));' &
[1] 3177
$ kill -USR1 3177
$ int(4294967288)

Вообще в Си так же, но где ПХП и где Си. Как и в Си, назад возвращается количество секунд, которые функция не «доспала» (time_nanosleep возвращает массив из секунд и наносекунд, а usleep — NULL). Правда под Виндой есть особенность — sleep при «недосыпе» всегда возвращает 192 (значение константы WAIT_IO_COMPLETION).

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

function bulletproof_sleep(int $seconds): bool
{
    $period = ['seconds' => $seconds, 'nanoseconds' => 0];

    while (array_sum($period) > 0) {
        $period = time_nanosleep($period['seconds'], $period['nanoseconds']);

        if (is_bool($period)) {
            return $period;
        }
    }

    return true;
}
14 мая   php   программирование

Как проверить не битая ли картинка

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

convert path_to_image null: 2>&- || echo File is corrupted

Если картинка path_to_image разрушена, то этот однострочник выведет «File is corrupted».

Утилита convert входит в состав пакета «Imagemagick» и достаточно распространена.

Принцип понятен, наверное: я прошу утилиту сконвертировать файл, указанный первым параметром, в псевдоформат NULL (указан до двоеточия во втором параметре) — это специальный формат используется, когда результат конвертации не нужен — обычно для замера производительности.

Входная картинка полностью разбирается кодеком, если это не удаётся сделать, то convert поругается в поток вывода ошибок (который я закрыл) и вернёт код возврата отличный от нуля, на что среагируют две вертикальные черты, которые запустят вторую команду.

Конечно, картинка должна быть в формате, который утилита понимает, но всё хоть немного распростанённое туда входит.

7 мая   bash   программирование

RabbitMQ и место на диске

Мы с сервером очередей RabbitMQ сталкиваемся редко — на нём исторически один продукт, остальные используют другой сервер. Впрочем, есть он не просил, проблем не обнаруживалось. А тут на днях появились странности — сервер на одном из проектов периодически как будто подвисал, принимал соединения, но команды словно «замерзали». Перезапуск помогал, но ненадолго.

Оказывается, каждые десять секунд RabbitMQ проверяет наличие свободного места, если оно меньше лимита (в нашем пакете, по-умолчанию — 1000000000 байт), то он останавливается и ждёт, когда место появится. Так он пытается предотвратить падение из-за нехватки дискового пространства.

Изучения логов результата не проносило и немудрено — сообщение о проблеме очень быстро оказывалось погребено под уймой другой информации, а оно, если лимит быстро исчерпывается, проскакивает ближе к началу старта сервера, очень легко пропустить. Помогло чтение форумов и эксперименты.

3 мая   программирование

Heredoc и nowdoc в PHP

Не очень-то люблю использовать heredoc и nowdoc в ПХП из-за того, что они портят форматирование (а оно важно в больших проектах). Хотя вещь удобная — внутри можно использовать оба вида кавычек без экранирования, это бывает актуально в эскуэль-запросах.

В Перле давным-давно можно нормально смещать эту конструкцию вправо, она сама обрежет лишние пробелы, а теперь и в ПХП сделали то же — с версии 7.3 можно будет делать вот так:

$values = [<<<END
      a
     b
    c
    END];

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

26 апреля   php   php7   программирование

Zabbix и sudo

Несколько дней назад админы у нас настраивали звонилку в системе мониторинга «Заббикс», чтобы получать уведомления о различных проблемах. Раньше всё было через ботов, а теперь, когда телеграм прищучили, понадобилась более традиционная сигнализация.

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

Подцепился через strace к нужному процессу, понаблюдал как он запускает команду. Оказывается для запуска команды делается дочерний процесс, ему передаётся строка для запуска строка, а дальше дочерний процесс жалуется «sudo: sorry, you must have a tty to run sudo» во второй поток (stderr), но это никуда не попадает — ни в логи, ни на экран. Странная архитектура.

Ошибка обозначает, что пользователю, из-под которого работает «Заббикс», запрещён sudo без терминала. Без терминала sudo не сможет отключить показ пароля на экране, поэтому по-умолчанию проверка включена. Так как у нас работает не человек и пароль нигде не набирается, то проверку надо выключить опцией !requiretty.

18 апреля   программирование

JavaScript и иврит

Выше прекрасный способ сломать мозг стыком трёх языков — английского, иврита и ДжаваСкрипта.

Как многим известно, в семитских языках текст записывается справа налево. Когда я лет 20 назад участвовал в разработке сайтов для некой арабской страны, это был тот ещё вызов моему внутреннему набору аксиом. Прокрутка слева, выделение текста в другую сторону, клавиша Backspace стирающая в противоположную сторону от стрелки, которая на ней нарисована, и прочие прелести.

Тогда кажется только Эксплорер умел поддерживать такое и переключался он атрибутом dir целиком, всем окном. Сейчас у нас есть Юникод и специальные коды смены направления текста. В одной строке можно сколько угодно его менять.

В ДжаваСкрипте выше именно это и сделано — английское const написано в привычном нам направлении, а дальше направление текста изменено и буквам иврита значения присваиваются справа налево. Выглядит психоделически, конечно.

Дополнение: в комментариях меня поправили. Оказывается коды смены направления тут не нужны — программы автоматически меняют направление, основываясь на том символы какого языка используются, просто в этот момент захватываются ещё и небуквенные символы — пунктуация, цифры и так далее.

2018   javascript   программирование

Негативное кеширование NFS

В реализациях сетевой файловой системы NFS есть так называемое «негативное кеширование». Эта штука портит много крови программистам, а суть её проста, как чихание — если вы обратились за файлом и его на момент обращения не было, то какое-то время этот результат будет закеширован.

Например, вы обращаетесь через NFS за файлом и если его нет, даёте серверу очередей задание на генерацию. Дожидаетесь выполнения задания и снова пытаетесь найти файл по тому же пути. Если обработка задания заняла секунды, то из-за негативного кеширования файл вы не увидите.

Теоретически этот кеш можно отключить (опцией lookupcache), практически же не всегда на оборудовании заказчика удаётся это сделать.

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

function clear_nfs_cache(string $filename): bool
{
    $tmpname = tempnam(dirname($filename), 'clear-cache'));
    return $tmpname === false ? false : unlink($tmpname);
}
2018   nfs   php   программирование

Структуры в Си и CGO

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

Для начала посмотрим как определяются и передаются структуры в Си. Полную спецификацию я приводить не буду, посмотрим на два распространённых способа. Первый используется в этом небольшом примере:

#include <stdio.h>
#include <math.h>

struct point {
	double x, y;
};

double point(struct point p) {
	return sqrt(pow(p.x, 2) + pow(p.y, 2));
}

int main() {
	printf("%g\n", point((struct point){1, 2}));
}

Тут можно обратить внимание на две особенности — во-первых, везде, где мы используем структуру, приходится использовать и ключевое слово struct, во-вторых, в коде существует и структура point, и функция с таким названием.

Это происходит потому, что имена структур обитают в своём собственном пространстве имён, на его использование и указывает struct. Там же живут имена объединений (union) и перечислений (enum). Имена функций находятся в общем пространстве, так что два таких определения с одним именем друг другу не мешают.

Структуры, объявленные таким образом видны через CGO компилятора Гоу как C.struct_name (C.struct_point в данном случае). К сожалению, тут есть особенность, о которую я споткнулся — если запрашиваемая структура не определена, то Гоу не выдаст ошибки, мы просто получим пустую структуру:

package main

import ("C"; . "fmt")

func main() {
	Println(C.struct_undefined{}) // ошибки не будет
}

Но программисты — люди ленивые, писать всюду struct неудобно. К счастью есть способ этого не делать — можно завести структуру, а потом дать ей имя в пространстве всех остальных типов. Это делается при помощи ключевого слова typedef позволяющего создавать алиасы типов:

struct point {
	double x, y;
};

typedef struct point point;

Теперь у нас есть два имени для одного типа — один в пространстве структур и прочего, второе — в общем пространстве. Можно даже сделать их одинаковыми, что очень удобно. Если второе имя нам ни к чему (бывают случаи, когда оно необходимо, но я не буду их тут рассматривать), то можно определить анонимную структуру и сразу дать имя этому типу в общем пространстве, так тоже будет работать.

Это нас подводит ко второму способу работы со структурами:

#include <stdio.h>
#include <math.h>

typedef struct {
	double x, y;
} point;

double veclen(point p) {
	return sqrt(pow(p.x, 2) + pow(p.y, 2));
}

int main() {
	printf("%g\n", veclen((point){1, 2}));
}

Ключевое слово struct перед типом уже не нужно, раз все имена в общем пространстве. Это же касается и CGO.

Как раз на эти грабли я и наступил — позабыл про эти все особенности. Когда я ещё писал на Си, всегда пользовался вторым способом, с именем в общем пространстве и привык к тому, что это и есть структуры.

2018   cgo   программирование   си   язык гоу

Структуры и CGO

Обновление: в комментариях в Фейсбуке мне бывший коллега попенял, что я забыл Си и что поведение правильное — тут объявляется анонимная структура, а потом к ней определяется тип. Верно, практики на Си у меня за последние годы почти не было, вымылось всё из памяти. Так что текст ниже читайте с учётом этого обновления заметки.

Каждый раз расстраиваюсь, когда сталкиваюсь с CGO. Идея-то прекрасная — встраивать в программы на Гоу сишные библиотеки почти без программирования на Си. Но частности всё убивают. То нельзя функцию по указателю передать, то вот убил час с утра сегодня, пытаясь починить ошибку в своей библиотеке go-gd.

Документация говорит нам, что сишные структуры доступны в Гоу через конструкцию C.struct_name, а её размер — через C.sizeof_struct_name.

Я так и пытался сделать и получал ошибки:

package main

/*
typedef struct {
    int x, y;
} test;
*/
import "C"
import . "fmt"

func main() {
    Println(C.struct_test{}) // «{}»? Что за…
    Println(C.sizeof_struct_test) // could not determine kind of name for C.sizeof_struct_test
}

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

Оказывается, и я набрёл на это случайно, в ходе экспериментов, struct просто не надо писать, тогда всё будет работать:

Println(C.test{}) // «{0 0}»
Println(C.sizeof_test) // 8

Если будет время, ещё поизучаю этот вопрос — посмотрю историю коммитов и всё такое, возможно была какая-то движуха, которую не отразили в документации, либо просто что-то сломали. Надеюсь кому-нибудь сэкономлю время этой заметкой.

2018   cgo   программирование   язык гоу

Баг в PHP 7.2 с output_add_rewrite_var

Для того, чтобы не подставлять в каждую ссылку и форму глобальные для проекта параметры запроса (идентификатор сессии, например), в ПХП используется техника, называемая Url Rewriting. У нас в проекте так передаётся токен против XSRF — благо есть возможность указать собственные параметры, которые надо передать и даже задать список тегов с атрибутами в которых это будет работать.

И при переходе на ПХП 7.2 словили неприятный баг, который уже поправлен в 7.1.9, но ещё почему-то не влит дальше: если урл не содержит ничего, кроме якоря, то параметры добавляются неправильно. Код для повторения бага такой:

<?php
output_add_rewrite_var('foo', 'bar');
?>
<a href="index.php">This is link</a>
<a href="#place">This is anchor</a>

В первую ссылку «foo=bar» добавится нормально, а во второй это будет не к месту, якорь будет испорчен:

<a href="index.php?foo=bar">This is link</a>
<a href="#place/?foo=bar">This is anchor</a>

Наверняка со стандартными сессиями ПХП, передаваемыми в урле, будет тот же результат, но я не пробовал.

Либо надо ждать обновления, либо поставлять вместо якоря текущий полный урл.

2018   php   php7   программирование
Ранее Ctrl + ↓