LINUX.ORG.RU

19
Всего сообщений: 79

Разрешённые символы в /etc/passwd

Экспериментируя с поддержкой нескольких пользователей в Haiku обнаружил что если в полях пользователя есть символ «:», то создаётся битый файл /etc/passwd (#16611). Система не должна допускать порчу базы пользователей при создании пользователя. Есть ли список запрещённых символов в /etc/passwd, есть механизмы экранирования? В man не могу ничего найти по этой теме кроме того, что почему-то в имени пользователя запрещены большие буквы.

 ,

X512 ()

Отучить дочерний процесс наследовать файловые дескрипторы

Здарова котаны!

Пишу софтину, она слушает tcp порт (9555). Так же она запускает дочерние процессы. Это та же программа, но запущенная с другим аргументом (–backend). Запускаю через QProcess из фреймворка Qt, я не знаю как он там внутри это делает. Дочерние процессы наследуют дескрипторы. Смотри на выхлоп lsof. Там три программы слушают один и тот же порт. Я почитал и вроде это не баг, а фича. Но мне всё равно это сильно мешает. Как от этого избавиться? Ппц, ну это вообще не в какие ворота.

➜ ~ lsof -i -P -n | grep 9555
retr 136948 ox55ff    7u  IPv4 438139      0t0  TCP *:9555 (LISTEN)
retr 136948 ox55ff    8u  IPv4 438196      0t0  TCP 127.0.0.1:9555->127.0.0.1:34772 (ESTABLISHED)
retr 137028 ox55ff    7u  IPv4 438139      0t0  TCP *:9555 (LISTEN)
retr 137028 ox55ff    8u  IPv4 438196      0t0  TCP 127.0.0.1:9555->127.0.0.1:34772 (ESTABLISHED)
retr 137030 ox55ff    7u  IPv4 438139      0t0  TCP *:9555 (LISTEN)
retr 137030 ox55ff    8u  IPv4 438196      0t0  TCP 127.0.0.1:9555->127.0.0.1:34772 (ESTABLISHED)

➜ ~ ps -Af | grep "retr"
ox55ff     136948  136946  0 16:17 pts/3    00:00:00 /home/ox55ff/projects/builds/program/retr/retr --frontend
ox55ff     137028  136948  0 16:17 pts/3    00:00:00 /home/ox55ff/projects/builds/program/retr/retr --backend
ox55ff     137030  136948  0 16:17 pts/3    00:00:00 /home/ox55ff/projects/builds/program/retr/retr --backend

 , ,

ox55ff ()

Отправка и получение датаграм SO_BROADCAST из другой сети

Всем доброго времечка, не болеть и не дурнеть от СМИ!

Взялся я писать клиента на Пингвине, который получает бродкаст udp-датаграмму и посылает ответ таким же способом. Нужно это для того, чтобы удаленный девайс, который может находится в другой подсети, доступ к которому ограничен, смог словить пакет, и прислать свой в ответ со своим ip-адресом для дальнейшей с ним работы. Ну вот незадача - если говорить об SOCK_DGRAM, Linux стоит на страже любых поползновений, акромя тех, что в его сетке.

Общение проходит между Windows и Linux (я пишу для этой стороны).

Удалось протиснуться, установив опцию SO_BINDTODEVICE, хоть этот сокет начал отправлять, но этот сокет не принимает, а может лишь отправлять (насколько я понял из мануалов). То, что сработало, ниже:


int main(int argc, char *argv[])
{
    int sockfd;
    struct sockaddr_in their_addr; // connector's address information
    struct hostent *he;
    int numbytes;
    int broadcast = 1;
    
    if (argc != 3) {
        fprintf(stderr,"usage: broadcaster hostname message\n");
        exit(1);
    }

    if ((he=gethostbyname(argv[1])) == NULL) {  // get the host info
        perror("gethostbyname");
        exit(1);
    }

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }
//    if ((sockfd = socket(AF_INET, SOCK_RAW, htons(ETH_P_IP))) == -1) {
//        perror("socket");
//        exit(1);
//    }

    // this call is what allows broadcast packets to be sent:
    if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast) == -1) {
        perror("setsockopt (SO_BROADCAST)");
        exit(1);
    }
    if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, "enp4s0", strlen("enp4s0")) == -1) {
        perror("setsockopt (SO_BINDTODEVICE)");
        exit(1);
    }

    their_addr.sin_family = AF_INET;	 // host byte order
    their_addr.sin_port = htons(SERVERPORT); // short, network byte order
    their_addr.sin_addr = *((struct in_addr *)he->h_addr);
    memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);


    if ((numbytes=sendto(sockfd, argv[2], strlen(argv[2]), 0,
                             (struct sockaddr *)&their_addr, sizeof their_addr)) == -1) {
        perror("sendto");
        exit(1);
     }


    printf("sent %d bytes to %s\n", numbytes,
           inet_ntoa(their_addr.sin_addr));


    close(sockfd);

Далее мои размышления пошли в сторону RAW-сокетов, что-то вроде этого

socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))

Как прочитал из мануалов, сырые сокеты могут получать датаграму еще до ее попадания в ядро, таким образом есть возможность отловить этот броадкаст, и вот моя следующая попытка это сделать:


    int fd;
    int bcast = 1;
    char recvString[MAXRECVSTRING+1]; /* Buffer for received string */
    int recvStringLen;                /* Length of received string */


    /* Create a best-effort datagram socket using UDP */
    if ((fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0)
        printf("socket() failed");

    struct sockaddr_ll sock;

    sock.sll_family     = AF_PACKET;
    sock.sll_protocol   = htons(ETH_P_IP);
    sock.sll_ifindex    = 0;
    sock.sll_hatype;
    sock.sll_pkttype    = PACKET_BROADCAST;;
    sock.sll_halen;
    sock.sll_addr[8];
    memset(sock.sll_addr, '\0', sizeof(sock.sll_addr));

    if (-1 == bind(fd, (struct sockaddr *) &sock, sizeof(sock)))
    {
        perror("bind");
        close(fd);
        exit(1);
    }
    if (-1 == setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast))) {
        perror("setsockopt (SO_BROADCAST)");
        exit(1);
    }

    while (true)
    {
    /* Receive a single datagram from the server */
    if ((recvStringLen = recvfrom(fd, recvString, MAXRECVSTRING, 0, NULL, 0)) < 0)
        printf("recvfrom() failed");

    recvString[recvStringLen] = '\0';
    printf("Received: %s\n", recvString);    /* Print the received string */

    }

    close(fd);

Только ОНО всё равно не работает, как надо: летит куча пакетов с первый байтом ‘E’. На просторах репозиториев находил, как люди устанавливают фильтры, но там жуткая морзянка из макросов, которую не каждый смелый человек отважится искать по крупицам. С posix сокетами раньше я не работал, использовал curl или qt, но нужда приперла. Прошу помочь навести порядок (что с чем мешать) с этими сокетами.

 , , ,

Tumyq ()

Openmp and POSIX in GCC 8.2.0

Добрый день! Поясните пожалуйста следующий вопрос: собрал Linux для ARM Cortex A9 (Zynq FPGA) с компилятором: gcc version 8.2.0

Пытаюсь скомпилировать С файл с примером для Openmp:

#include <omp.h>
#include "stdio.h"
int omp_get_thread_num();
int main()
{
	# pragma omp parallel
	{
	  printf("Thread rank: %d\n", omp_get_thread_num());
	}
}

появляется ошибка

fatal error: omp.h: No such file or directory

вывод команды gcc -v

Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/arm-xilinx-linux-gnueabi/8.2.0/lto-wrapper Target: arm-xilinx-linux-gnueabi Configured with: ../../../../../../work-shared/gcc-8.2.0-r0/gcc-8.2.0/configure --build=x86_64-linux --host=arm-xilinx-linux-gnueabi --target=arm-xilinx-linux-gnueabi --prefix=/usr --exec_prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --libexecdir=/usr/libexec --datadir=/usr/share --sysconfdir=/etc --sharedstatedir=/com --localstatedir=/var --libdir=/usr/lib --includedir=/usr/include --oldincludedir=/usr/include --infodir=/usr/share/info --mandir=/usr/share/man --disable-silent-rules --disable-dependency-tracking --with-libtool-sysroot=/scratch/petalinux-yocto/yocto_downloads_2019.1_zynq-generic/build_zynq-generic/tmp/work/cortexa9t2hf-neon-xilinx-linux-gnueabi/gcc/8.2.0-r0/recipe-sysroot --with-gnu-ld --enable-shared --enable-languages=c,c++ --enable-threads=posix --enable-multilib --enable-default-pie --enable-c99 --enable-long-long --enable-symvers=gnu --enable-libstdcxx-pch --program-prefix=arm-xilinx-linux-gnueabi- --without-local-prefix --enable-lto --disable-libssp --enable-libitm --disable-bootstrap --disable-libmudflap --with-system-zlib --with-linker-hash-style=gnu --enable-linker-build-id --with-ppl=no --with-cloog=no --enable-checking=release --enable-cheaders=c_global --without-isl --with-float=hard --with-sysroot=/ --with-build-sysroot=/scratch/petalinux-yocto/yocto_downloads_2019.1_zynq-generic/build_zynq-generic/tmp/work/cortexa9t2hf-neon-xilinx-linux-gnueabi/gcc/8.2.0-r0/recipe-sysroot --with-gxx-include-dir=/usr/include/c++/8.2.0 --without-long-double-128 libgcc_cv_powerpc_float128=no --disable-static --enable-nls --enable-initfini-array --with-arch=armv7-a+fp Thread model: posix gcc version 8.2.0 (GCC)

Файлы с POSIX компилируются без проблем, но очень хочется иметь поддержку Openmp. Какие существуют возможные варианты решения данной ситуации?

 , , ,

Alexey_Rostov ()

Секурный канал связи между Linux/OpenBSD и Nuttx

Подскажите, как безопасно рулить микроконтроллером с Nuttx на борту с другого хоста через сетку.

Т.е. канал по безопасности должен быть не хуже SSH с парами ключей и другими залочками.

А SSH нельзя собрать для Nuttx? MCU не осилит криптуху? Какой-нибудь dropbear с не самыми сильными шифрами? Nuttx пока не интегрирован с криптотокенами типа Rutoken?

Какие существуют дистрибутивы с ядром Nuttx? Какие там есть готовые пакеты? В нем есть stunel, файрвол и т.п.?

httpd вроде есть.

XML-RPC можно зарядить через HTTPS в связке Linux->Nuttx с залочкой на свои серты без цепочек доверия и другого MITM?

Ожидать появление поддержки ядра Nuttx (типа kfreebsd) в Gentoo нереально? Конечно с поддержкой только небольшого количества консольных и демонических пакетов Gentoo.

 , , ,

a_buchinskiy ()

Новое небольшое POSIX ядро для слабеньких процов типа Z80

https://github.com/EtchedPixels/FUZIX

Кто (собирательно, причины) это делает, спонсирует?

Какие перспективы?

 ,

simoshina ()

Подскажите регулярку POSIX

Подскажите , корректная ли эта POSIX регулярка? Или нужно как-то иначе. Цель - найти хеш (32 символа, нижний регистр + цифры). В PCRE это [a-z0-9]{32}

Пишу код вида

reti = regcomp(&regex, "[0-9a-z]{32}", REG_NEWLINE);
if (reti) 
...

reti = regexec(&regex, lp, 0, NULL, 0);
if (!reti) {
    log(0,L"Match",NULL,0);
}
else if (reti == REG_NOMATCH) {
    log(0,L"No match",NULL,0);
}

Но не работает, хотя в lp (это указатель на память) строка 100% присутствует.

 ,

zer0cat ()

$(read -r var;echo $var)

Подскажите альтернативу этой конструкции в 1 команду, без переменных, спасибо. Я начинаю думать, что и так сойдёт.

 , , , ,

linuxnewbie ()

Как проверить, есть ли байты в буфере

На вход программе (через Unix Domain Socket или через stdin) приходит сообщение произвольной длины, после чего отправитель ожидает ответа.

Так как сообщение произвольной длины, не получается использовать read(length) или recv(length) — если длина сообщения окажется кратной length, то случится взаимоблокировка (клиент ждёт ответа, сервер ждёт продолжения сообщения).

Решением было бы посмотреть, есть ли в буфере байты.

man ioctl предлагает такое решение:

while ((ioctl(sock, I_NREAD, &num) >= 0) && num > 0)
    /* дописать даные из буфера ввода в буфер в памяти */

, но это не работает для stdin (ошибка ENOTTY):

if ((ioctl(0, I_NREAD, &num) >= 0) && num > 0) /* ошибка ENOTTY */

Есть ли общий способ? Или для сокета и для stdin обязательно надо применять разные подходы (если так, то какие)?

EDIT: пример кода «клента» (на пхп):

$proc_handle = proc_open('./run', [['pipe', 'r'],['pipe', 'w']], $pipes);

foreach ($test_cases as $request => $response)
{
    echo "Test: ".$request;
    fwrite($pipes[0], $request);
    fflush($pipes[0]);

    echo "Answer: ".fgets($pipes[1]);
    echo "\n";
}

EDIT: Пример решения: перевод stdin в неблокирующий режим (подсказал u/Elyas, решение взято с https://stackoverflow.com/a/30548692):

#include <stdio.h>

#include <unistd.h>
#include <fcntl.h>

static int counter = 0;

void updatecounter(void)
{
	int n;
	char buf[100];
	while (1)
	{
		if ((n = read(0, buf, 100)) <= 0) break;
		counter += n;
	}
}

int main(void)
{
	fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);

	while (1)
	{
		updatecounter();
		printf("%d\n", counter);
		sleep(2);
	}

	return 0;
}

EDIT: Пример решения: использование poll (подсказал u/mittorn):

#include <stdio.h>

#include <unistd.h>
#include <poll.h>

static int counter = 0;

void updatecounter(void)
{
	char buf[100];
	struct pollfd pollfds[1] = {0, POLLIN, 0};

	while (1)
	{
		if (!poll(pollfds, 1, 0)) break;
		counter += read(0, buf, 100);
	}
}

int main(void)
{
	while (1)
	{
		updatecounter();
		printf("%d\n", counter);
		sleep(2);
	}

	return 0;
}

EDIT: решение от u/kardapoltsev : указывать длину сообщения перед его началом

EDIT: оба решения проверены на сокетах.

 , , ,

peterstein ()

Разработка многопоточного приложения средствами POSIX в ОС Linux

Ребят, учусь в УУМЗ, и препод решил всех удивить своими познаниями в программировании. Но с нами ими не поделился, только дал задание. Помогоите пожалуйста написать код, удовлетворяющий заданию. Вершины графа являются точками запуска/завершения потоков, дугами обозначены сами потоки, длину дуги следует интерпретировать как ориентировочное время выполнения потока. Реализовать последовательно-параллельный запуск потоков в ОС Linux с использованием средств POSIX для запуска и синхронизации потоков. Запрещается использовать какие-либо библиотеки и модули, решающие задачу кроссплатформенной разработки многопоточных приложений (std::thread, Qt Thread, Boost Thread и т.п.). Самостоятельно выделить на графе две группы с выполняющимися параллельно потоками. Первая группа не синхронизирована, параллельное выполнение входящих в группу потоков происходит за счет использования искусственной задержки. Величина задержки должна быть как можно меньше. При выполнении операций ввода-вывода перед входом в критическую секцию потоки должны захватывать мьютекс. Вторая группа синхронизирована семафорами: входящий в групу поток передает управление другому потоку после каждой итерации. Последовательное выполнение потоков должно обеспечиваться за счет использования семафоров. Все потоки должны запускаться одновременно из функции k1_init(), без задержек, один за другим. Использовать функцию pthread_join() разрешается только в функции k1_init() для ожидания завершения работы всех запущенных потоков. Вот этот граф. http://d.radikal.ru/d38/1903/45/c004a55e6571.png

 , ,

ishka ()

Получить exit code, с которым завершилась программа в середине скрипта

Есть такой скрипт:

if ! myprgm; then
  echo "fail"
  exit 1
fi

Я хочу сохранять то, что выводит myprgm в stdout в файл stdout.txt и для каждой строчки печатать в stdout «o», а вывод в stderr сохранять в stderr.txt и и для каждой строчки печатать в stdout «x». Вот что у меня получилось:

mkfifo out
mkfifo err

process_out() {
  while read line; do
    echo "${line}" >> stdout.txt
    /bin/echo -n "o"
  done < out
}

process_err() {
  while read line; do
    echo "${line}" >> stderr.txt
    /bin/echo -n "x"
  done < err
}

if ! myprgm > out 2> err & process_out & process_err; then
  echo "fail"
  exit 1
fi
Но это не будет работать, потому что $?, от которого зависит срабатывание if будет принадлежать функции process_err, а не myprgm.

Как можно грамотно переписать этот говнокод? Хотелось бы, чтобы работало в POSIX-совместимом шелле.

 , , ,

CYB3R ()

Разделяемая память и ее защита.

Добрый вечер, форумчане!

Имеется вопрос, есть в проекте (операционка QNX4.25) несколько разделяемых объектов памяти, для взаимодействия между процессами. Есть один писатель в каждую из них и несколько читателей, я написал несколько функций оберток вида:

void XXXX_ShmemLock()
{
	assert( g_Shmem );
	sem_wait( &g_Shmem->lock );
}

void XXXX_ShmemUnlock()
{
	assert( g_Shmem );
	sem_post( &g_Shmem->lock );
}

void XXXX_ShmemCopy( void *destination, const void *source, const size_t num )
{
	XXXX_ShmemLock();
	_disable();
	memcpy( destination, source, num );
	_enable();
	XXXX_ShmemUnlock();
}

Дальнейшие операции более высокого уровня, выполняются путем вызова функций-оберток, как пример приложу:

void XXXX_ReadADC( const ADCChannel_t channel, double *voltage )
{
	assert( channel < ADC_ChannelsNum );
//	*voltage = g_Shmem->Vin[channel];
	XXXX_ShmemCopy( (void *)voltage, (const void *)&g_Shmem->Vin[channel], sizeof(g_Shmem->Vin[channel]) );
}

void XXXX_WriteADC( const ADCChannel_t channel, const double voltage )
{
	assert( channel < ADC_ChannelsNum );
//	g_Shmem->Vin[channel] = voltage;
	XXXX_ShmemCopy( (void *)&g_Shmem->Vin[channel], (const void *)&voltage, sizeof(voltage) );
}
P.S. Насколько рационален такой подход?

Так вот, как видно в функции XXXX_ShmemCopy, копирование данных в область и из нее, производится вызовом memcpy, обернутым в семафор и дополнительным отключением прерываний на момент копирования, чтобы обеспечить атомарность операции. Есть ли вообще смысл в такой перестраховке на однопроцессорной системе? Минус в использовании _disable()/_enable() при больших объемах копирования (которых скорее всего не будет), на момент выполнения операции, стопорится работа всей системы, или если вызовов XXXX_ShmemCopy будет много (а их скорее всего будет много), то рискуем то и делать что бесконечно выключать/включать прерывания. Так же есть ли необходимость оборачивания простых операций вида «Область->переменная = что-то записать», понятное дело не делая справа от «=» сложных операций, будет ли такая операция выполнена атомарно?

 , , , ,

subuser ()

Проверка соединения.

Добрый день уважаемым форумчанам! Есть под оффтопиком функия в Api InternetGetConnectedState, которая позволяет чекнуть - есть ли инет на тачке. Собственно вопрос к знающим - как такое же проверить по Линуксом? Пинг отваливается, ибо нужен рут. Каждый раз закачивать страничку и смотреть что пришло - тоже кажется так себе вариантом. Быть может есть вариант более...изящный? Ах, да. Без QT.

 , ,

CynicRus ()

Разница между man 2 и man 3

Столкнулся с очень неприятной проблемой.

Если одна и та-же функция описана как секции мануала 2 - Unix and C system calls так и в 3 - C library routines for C programs.

Но описание поведеня у функции разное. Как определить поведение у себя в программе? Или как явно сообщить что я хочу использовать именно systemCall или C library function? Ведь и там и так одна и таже сигнатура функции и одни и теже инклуды.

Конкретно я столкнулся в функцией waitpid.

В man 2 говорится так :

The waitpid() system call suspends execution of the calling process until a child specified by pid argument has changed state.

В man 3 говорится так :

The wait() function shall suspend execution of the calling thread until status information for one of the terminated child processes of the calling process is available ... The waitpid() function shall be equivalent to wait() if the pid argument is (pid_t)-1 and the options argument is 0.

Я сначала был уверен в том, что на waitpid уснут потоки моего процесса (согластно man2), а по факту усыпал только один ( согластно man3)

 , , , ,

letitbe ()

как сделать интервальные счетчики в проге на си

всем привет!

Вопрос следующий.

Каким самым верным способом можно реализовать подсчет некоторых событий в программе и сохранять эти данные допустим в файл по истечении заданного интервала?

Например, в коде идет подсчет какого либо события. Как каждый час сохранять этот счетчик?

Сигналы по таймеру? или есть что то другое, более правильное?

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

Заранее спасибо!

 ,

elmir_k ()

«rm -f», который мы заслужили

POSIX will say in a future version that calling «rm -f» with no argument is OK; and this sensible behaviour seem to be already very widespread in «the wild» (and possibly lacking only on those systems that are well on their way to obsolescence).

http://horis.kanardia.eu/rok2/Builder/igep_old/buildroot-2015.08-rc1/output/b...

Это было обнаружено в тарболе свежего automake 1.16.1, где это также подчёркивается и в файле NEWS наряду с другими планами для automake 2.0.

Просто «rm -f», кстати, совершенно ничего не делает. Абсолютно. Но, это зачем-то протаскивают в POSIX...

 ,

saahriktu ()

BSD sed - добавление строки перед заданной

Думаю, что для таких простых задач sed вполне оправдан. Но столкнулся с несовместимостью GNU реализации седа с posix.

В гну для добавления строки перед маркером я бы сделал так:

sed -i "/\;Marker/i blahblah" file

Как это переписать под BSD?

 , , , ,

mammuthus ()

Что в коробке?

Недавно добыл ТВ-коробку от ВЕЛИКОГО RT,и при первом запуске узнал знакомый до боли в сердце запуск Всеми Любимой Системы. Интересно, прав ли я? И какой дистр стоит там? И POSIX-совместимая это система? Вот бы расковырять бы её.

 ,

kshmr ()

Чем через открытый дескриптор определить физическую потерю устройства на которое он указывает?

Например. Есть /dev/ttyUSB0, на нем открытый файловый дескриптор (POSIX open), на файловом дескрипторе по кругу вызываются select с read-ом. Вдруг, внезапно выдергиваю usb-кабель, и в результате:
1. /dev/ttyUSB0 пропадает;
2. select() начинает бесконечно выдавать FD_ISSET() == 1 для готовности чтения на нашем декскрипторе.
3. read() при этом все время возвращается прочитав 0 байт.

Такое поведение наблюдается на платформе которая имеется в моём распоряжении (x86_64, linux 4.0.4, GNU libc 2.18), но я не знаю как программа поведёт себя на другой платформе.

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

 ,

normann ()

Есть ли что-то вроде POSIX queue но с возможностью итерироваться?

Хочу сделать свою реализацю логирования. Будет как-то так: Етсь чередь. В нее процессы пишут свои сообщения. Есть сервис, который эти сообщения из очереди вынимает и складывает в файлик. Может при этом не писать, например, отладочные сообщения. Тут пока все просто. Далее нужна возможность сделать что-то вроде tail -f -n 1000, но с фильтрами по уровню, источнику сообщений или с поиском подстроки. И тут появлется желание иметь очередь не простую, а что-то вподе циклического буффера, но с возможностью посмотреть уже вынутые сообщения. Или можно по другому: список в котором записи имеют хендлы. По хендлу можно узнать, жива ли еще эта запись. Елси запись ужа удалена из памяти, можно получить самую старую запись

 , , ,

vromanov ()