Что такое ошибка 1 success

Время на прочтение
9 мин

Количество просмотров 3.4K

image

Всем начинающим программистам всегда рассказывают о важности правильного формирования сообщения об ошибках. Всегда говорят о том, что если программа что-то не смогла сделать, то она должна ясно и недвусмысленно рассказать почему это произошло. Рассказывают о важности контроля возвращаемого значения вызываемых функций. При этом даже компиляторы научились выдавать предупреждения если возвращаемое определенными функциями значение игнорируется. Надо сказать что важность обработки ошибок современными программистами принята. Временами это приводит к занятным казусам, как на КДПВ (взята здесь). Мне в реальной жизни несколько раз приходилось сталкиваться с подобными странными диагностическими сообщениями. О последнем случае и методах преодоления такой вот диагностики я и хочу рассказать. Если стало интересно милости прошу под кат. Опытные программисты наверняка не откроют для себя ничего нового, но пофилософствовать о разработке ПО точно смогут.

Для тех, кому «букв много»

  • правильно обрабатывать возвращаемые функцией read() значения важно
  • не забываем проверять актуальность используемого софта (а OpenSource особенно)
  • многофункциональные программы-комбайны в большинстве случаев одинаково неудобны для решения любой из допустимых задач. С другой стороны плодить «москитный флот» не лучшая идея

А вообще у меня грустная новость. Не будет больше никаких картинок. Мы опустимся на уровень системной консоли Linux и будем жить там. При этом будем радоваться. Ибо проект, с которым предстоит работать — достаточно известный загрузчик U-Boot. Проект с открытым исходным кодом, поддерживаемый компанией DENX Software Engineering. Потому порадуемся тому, что у нас есть консоль, есть системное окружение и вообще жизнь кипит. Потому как при работе с этим проектом, как правило, нет ничего подобного — сплошные области памяти, пересылки байт из одного места в другое да ожидание готовности периферии. Но, к частью, эта часть уже выполнена и есть вполне себе рабочий загрузчик для железки. Пора заняться украшениями, которые позволят прикладным программистам как-то влиять на процесс загрузки системы. Ничто не предвещает проблем. Задача давно решена и активно используется таким популярным проектами как OpenWRT и множеством других, чуть менее известных.

Суть очень проста. U-Boot корректирует свое поведение в зависимости от переменных среды. Переменные среды между перезагрузками могут быть сохранены в энергонезависимой памяти. Утилиты командной строки fw_printenv и fw_setenv позволяют соответственно выводить и менять их значение прямо из Linux. Все. В принципе большего и не требуется. Как всегда инструкцию мы будем читать «когда дым рассеется». Да и откуда здесь взяться дыму? Дым весь был выпущен когда загрузчик под эту плату адаптировали. Потому смело набираем команду «fw_printenv», потому как она-то точно ничего сломать не может.

localhost ~ # fw_printenv
Cannot open /dev/mtd1: No such file or directory
localhost ~ # fw_printenv --help
Usage: fw_printenv [OPTIONS]... [VARIABLE]...
Print variables from U-Boot environment

 -h, --help           print this help.
 -v, --version        display version
 -c, --config         configuration file, default:/etc/fw_env.config
 -n, --noheader       do not repeat variable name in output
 -l, --lock           lock node, default:/var/lock

Ну ожидаемо. Конечно. Мы не указали где именно хранятся переменные среды. А «быстрая помощь» однозначно говорит о том, что указать в командной строке и не получится. Надо править конфигурационный файл /etc/fw_env.config. Формат файла довольно простой и интуитивно понятный. Для того, чтоб не создавать самому себе (и окружающим) трудностей я разместил переменные среды U-Boot в самом доступном месте, которое только можно себе придумать. Конкретно в файле uboot.env первого раздела основного накопителя, отформатированного реально переносимой файловой системой vfat (она же FAT-32). И проверил. Из консоли U-Boot переменные сохраняются в файл, при старте из него читаются. Красота. Осталось только дать возможность их править из Linux. Раздел c файлом uboot.env, а еще ядром, файлом дерева устройств, и некоторым дополнительным наполнением критичным для работы системы совершенно логично монтируется к /boot. Потому совершенно не сомневаясь комментирую строчки 11 и 12 (/dev/mtd1 и /dev/mdt2 соответственно) и убираю комментарий со строчки 30 (/boot/uboot.env) в конфигурационном файле.

# VFAT example
/boot/uboot.env 0x0000          0x4000

Все. Вроде все подготовительные операции выполнены. Дубль два.

localhost ~ # fw_printenv
Read error on /boot/uboot.env: Success

Ну здравствуй, КДПВ. Первая разумная мысль, которая посещает любого Linux’оидника в такой ситуации — а что с правами. Впрочем, наш лозунг «Слабоумие и отвага» — мы работаем от root’а. Логично. Чего бояться человеку, который для железки делает загрузчик и имеет самый что ни на есть физический (с паяльником) доступ к плате? А может файла просто нет? Забыл в консоли U-Boot’а сказать «saveenv»? Проверим…

localhost ~ # ls -l /boot/uboot.env
-rwxr-xr-x 1 root root 8192 Dec  2 13:22 /boot/uboot.env

Нет, есть он. И даже читаться может всем миром (ай, как не хорошо). Интересно, а если его не будет?

localhost ~ # mv /boot/uboot.env /boot/uboot.env.bak
localhost ~ # fw_printenv
Cannot open /boot/uboot.env: No such file or directory
localhost ~ # mv /boot/uboot.env.bak /boot/uboot.env

Логично. Тут все правильно. Ладно, тяжело вздохнули и… Это наш кактус, нам его и грызть. Хорошо хоть исходники есть. Надо глянуть, что там у нас происходит. Может мысли какие появятся? Очень быстро находим строку 950 в файле tools/env/fw_env.c:

        lseek(fd, blockstart + block_seek, SEEK_SET);

        rc = read(fd, buf + processed, readlen);
        if (rc == -1) {
            fprintf(stderr, "Read error on %s: %sn",
                DEVNAME(dev), strerror(errno));
            return -1;
        }
        if (rc != readlen) {
            fprintf(stderr,
                "Read error on %s: Attempted to read %zd bytes but got %dn",
                DEVNAME(dev), readlen, rc);
            return -1;
        }

Нет. Тут вполне себе классическая обертка над функцией read(). Практически прямиком из учебника. И, судя по поведению итоговой программы не остается сомнений в том, что read() возвращает -1, но при этом errno остается нулевым. Подождите. Что это за скрежет раздается? А, это мозги шевелиться начали… Хорошо…

Ну что, можно почитать мануал на read? Не, ерунда… Уж вроде про read-то все читано-перечитано. Все мыслимые и немыслимые варианты ошибок с функцией read() давно известны. Не должно быть такого. Что делаем дальше? Правильно, раз исходники не дают ответ — пусть его даст сама система.

localhost ~ # strace fw_printenv
execve("/usr/bin/fw_printenv", ["fw_printenv"], 0x7ebf2400 /* 28 vars */) = 0
brk(NULL)                               = 0x2118000
uname({sysname="Linux", nodename="localhost", ...}) = 0
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=42265, ...}) = 0
mmap2(NULL, 42265, PROT_READ, MAP_PRIVATE, 3, 0) = 0x76f14000
close(3)                                = 0
openat(AT_FDCWD, "/lib/libc.so.6", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
read(3, "177ELF11133(1f~1004"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1286448, ...}) = 0
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x76f12000
mmap2(NULL, 1356160, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x76da1000
mprotect(0x76ed7000, 65536, PROT_NONE)  = 0
mmap2(0x76ee7000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x136000) = 0x76ee7000
mmap2(0x76eea000, 8576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x76eea000
close(3)                                = 0
set_tls(0x76f12ca0)                     = 0
mprotect(0x76ee7000, 8192, PROT_READ)   = 0
mprotect(0x4a9000, 4096, PROT_READ)     = 0
mprotect(0x76f1f000, 4096, PROT_READ)   = 0
munmap(0x76f14000, 42265)               = 0
openat(AT_FDCWD, "/var/lock/fw_printenv.lock", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
flock(3, LOCK_EX)                       = 0
brk(NULL)                               = 0x2118000
brk(0x2139000)                          = 0x2139000
openat(AT_FDCWD, "/etc/fw_env.config", O_RDONLY) = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=1342, ...}) = 0
read(4, "# Configuration file for fw_(pri"..., 4096) = 1342
read(4, "", 4096)                       = 0
close(4)                                = 0
openat(AT_FDCWD, "/boot/uboot.env", O_RDONLY) = 4
fstat64(4, {st_mode=S_IFREG|0755, st_size=8192, ...}) = 0
close(4)                                = 0
openat(AT_FDCWD, "/boot/uboot.env", O_RDONLY) = 4
_llseek(4, 0, [0], SEEK_SET)            = 0
read(4, "n.'202__INF0__=Ravion-V2 I.MX6 CPU"..., 16384) = 8192
write(2, "Read error on /boot/uboot.env: S"..., 39Read error on /boot/uboot.env: Success
) = 39
close(4)                                = 0
flock(3, LOCK_UN)                       = 0
close(3)                                = 0
exit_group(1)                           = ?
+++ exited with 1 +++
localhost ~ #

Люблю Linux. Ай какая красота. Все сразу встало на свои места. Ладно, согласен — не все. Но уже что-то. Самое интересное здесь:

openat(AT_FDCWD, "/boot/uboot.env", O_RDONLY) = 4
_llseek(4, 0, [0], SEEK_SET)            = 0
read(4, "n.'202__INF0__=Ravion-V2 I.MX6 CPU"..., 16384) = 8192
write(2, "Read error on /boot/uboot.env: S"..., 39Read error on /boot/uboot.env: Success
) = 39

Пытаемся прочитать 16384 (16K), а можем только 8192 (8K). В принципе всё. Бинго. Поднимаемся выше и смотрим размер файла. Да, он действительно 8192 байта. Поднимаемся еще выше и смотрим строку в конфиге. Смещение 0, длина 0x4000 или 16384. Исправляем на 0x2000

# VFAT example
/boot/uboot.env 0x0000          0x2000

Да, черт возьми я очень стар. По мне U-Boot’у для переменных среды и килобайта хватит. Еще тут драгоценную память просто так расходовать. Что вы хотите. Коренной житель Питера. Нам с детства вбили что хлеб (ресурсы) надо беречь. А выбрасывать его — это не ценить память погибших из-за его отсутствия в блокаду. И да, мы такие. Спасибо реальным ветеранам, спасибо городским музеям. Которые не смотря ни на что сохраняют память о тех страшных временах. Надеюсь и мои дети этому научатся.

Так вот — о переменных среды для U-Boot. Ну два килобайта. Ну, ладно — четыре. Куда больше? Что там можно писать в таких количествах (и главное зачем)? Потому действительно — был момент когда выделенные по умолчанию 16К урезал до 8. Еще думал — куда столько? Ладно, лирику в сторону — проверяем.

localhost ~ # fw_printenv
__INF0__=Ravion-V2 I.MX6 CPU Module BSP package
__INF1__=Created: Alex A. Mihaylov AKA MinimumLaw, MinimumLaw@Rambler.Ru
[…]
boot_os=1
localhost ~ #

Работает. И даже fw_setenv работает.

localhost ~ # fw_setenv boot_os 0; fw_printenv boot_os
boot_os=0

Можно ставить точку? Думаю нет. Остался один важный вопрос, который уважающий себя программист не имеет права оставить без внимания. Как думаете, какой вопрос?

Правильно думаете. Если посмотреть на код, который лежит в репозитарии U-Boot, то можно легко заметить, что такая ситуация должна корректно отрабатываться. Я выше специально не стал вырезать этот кусок. Больше того, strace совершенно честно и открыто говорит что read возвращает значение 8192. Так почему же мы оказываемся в ветке с ошибкой чтения? Ведь 8192 никак не может равняться -1.

Давайте разбираться. Первая мысль, которая приходит в голову — подождите, но ведь Das U-Boot это динамично развивающийся проект. Может быть мы смотрим репозитарий с последним релизом загрузчика. Но та часть, которую используем мы совсем не обязана быть последней. Она часть пользовательского окружения операционной системы. Это я адаптирую последнюю версию загрузчика, чтоб пульс проекта ощущать. А авторы сборок прикладного ПО скорее за стабильность ратуют. Потому она наверняка последней и не будет. Проверяем.

localhost ~ # fw_printenv --version
Compiled with U-Boot 2019.10
localhost ~ #

Ага! А у меня в работе последняя стабильная (2020.10). Разница в год. Огромная дистанция для динамично развивающегося OpenSource проекта. А давайте посмотрим .

        lseek(fd, blockstart + block_seek, SEEK_SET);

        rc = read(fd, buf + processed, readlen);
        if (rc != readlen) {
            fprintf(stderr, "Read error on %s: %sn",
                DEVNAME(dev), strerror(errno));
            return -1;
        }

Ну да. Так и есть. Уже все исправили. Обидно. Такой красивый баг был. Ладно, на нашу жизнь багов еще припасено. Только успевай разбирать.

Но ведь и это ещё не все. А давайте заглянем в файл «uboot.env»

localhost ~ # hexdump -C /boot/uboot.env
00000000  0a 43 62 eb 5f 5f 49 4e  46 30 5f 5f 3d 52 61 76  |.Cb.__INF0__=Rav|
00000010  69 6f 6e 2d 56 32 20 49  2e 4d 58 36 20 43 50 55  |ion-V2 I.MX6 CPU|
00000020  20 4d 6f 64 75 6c 65 20  42 53 50 20 70 61 63 6b  | Module BSP pack|
00000030  61 67 65 00 5f 5f 49 4e  46 31 5f 5f 3d 43 72 65  |age.__INF1__=Cre|
[...]
00000720  3d 71 70 00 76 65 6e 64  6f 72 3d 72 61 76 69 6f  |=qp.vendor=ravio|
00000730  6e 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |n...............|
00000740  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00002000
localhost ~ #

Вполне очевиден факт — оценка размера блока достаточного для хранения переменных среды, которая была дана мною выше, вполне справедлива. На данный момент использовано 1837 байт (0x7031 – 4) и формат блока довольно простой. Первые 4 байта CRC32, а дальше разделенные нулем переменные в формате переменная=значение. Другими словами поведение утилиты все равно вызывает вопросы. Ну напишет она что размер файла меньше ожидаемого и завершится с ошибкой. Но ведь это не правда. Все значимые и важные данные в него (даже в двухкилобайтный!) вполне бы влезли. Может все же стоит поправить?

Увы нет. И причина этому вполне банальна. Переменные в U-Boot могут храниться в самых разных местах. Файл на vfat разделе это самое приятное место. За это его и выбрал. Но в том же OpenWRT нет таких удобных накопителей. Там SPI-flash. И под переменные среды выделяется целый сектор. Но и тут все может быть не так плохо. Сектор целиком надо стирать. Писать можно частями. Беда с системами, которые используют dataflash или некоторые варианты raw-NAND накопителей. Т.е. с теми системами, которым помимо данных нужна еще и контрольная информация для контроля целостности и исправности. Вот они обязаны писать весь блок целиком.

Получается интересная альтернатива. Или мы для разных накопителей делаем разные утилиты для работы с переменными среды или… Делаем одинаково неудобно для всех. Посмотрите на код утилиты. Посмотрите на формат сохраняемых данных. К сожалению почти всегда так и получается. Хотели, чтоб всем было одинаково хорошо. На выходе всем получилось одинаково плохо. Впрочем, свою функциональность решение вполне обеспечивает. Пусть так и остается.

Вот так неожиданно и получилось легонькое пятничное чтиво. Было бы интересно посмотреть сколько времени суммарно прожила эта ошибка, но… Уже не настолько интересно, чтоб тратить на это время. Как говаривал классик: «Сказка ложь, да в ней намек. Добрым молодцам урок.» Спасибо за то, что дочитали.

P.S.
Пользуясь случаем передаю привет CodeRush Еще раз благодарю за приглашение на Habr. И да, всегда хочется писать о серьезном — о компиляторах, о безопасном программировании непосредственно по железу. А сил хватает только на легкое пятничное чтиво. Ладно, будем считать, что начало положено. Большое путешествие всегда начинается с маленького шага.

  • Классы состояния
  • Коды ответов сервера
  • 1xx
  • 2xx
  • 3xx
  • 4xx
  • 5xx

HTTP status code — (с англ.) код состояния HTTP; часть первой строки ответа сервера при запросах по протоколу HTTP. Он (код ответа) представляет собой целое число из трёх цифр, первая из которых указывает на класс состояния. За кодом ответа обычно следует, отделённая пробелом, поясняющая фраза на английском языке, которая разъясняет человеку причину именно такого ответа:

200 OK

404 Not Found

500 Internal Server Error

Классы состояния

1xx: Informational
Информационные ответы — запрос от клиента принят, работа продолжается.
2xx: Success
Успех — запрос от клиента принят, понятен и может быть выполнен.
3xx: Redirection
Перенаправление — для выполнения запроса необходимы дальнейшие действия.
4xx: Client Error
Ошибка клиента — запрос составлен неверно или не может быть выполнен.
5xx: Server Error
Ошибка сервера — запрос правильный, но сервер не может его выполнить.

Коды ответов сервера

Номер Поясняющая фраза Перевод
1xx Informational Информационные ответы
100 Continue Продолжить
101 Switching Protocols Переключение протоколов
102 Processing Идёт обработка
103-199 Не назначены
2xx Success Успех
200 OK Хорошо
201 Created Создано
202 Accepted Принято
203 Non-Authoritative Information Информация не авторитетна
204 No Content Нет содержимого
205 Reset Content Сбросить содержимое
206 Partial Content Частичное содержимое
207 Multi-Status Многостатусный
208 Already Reported Уже сообщили
209-225 Не назначены
226 IM Used Использовано IM
227-299 Не назначены
3xx Redirection Перенаправление
300 Multiple Choices Множество выборов
301 Moved Permanently Перемещено навсегда
302 Found Найдено
303 See Other Смотреть другое
304 Not Modified Не изменялось
305 Use Proxy Использовать прокси
306 Не используется
307 Temporary Redirect Временное перенаправление
308 Permanent Redirect Постоянное перенаправление
309-399 Не назначены
4xx Client Error Ошибка клиента
400 Bad Request Плохой, неверный запрос
401 Unauthorized Неавторизован
402 Payment Required Необходима оплата
403 Forbidden Запрещено
404 Not Found Не найдено
405 Method Not Allowed Метод не поддерживается
406 Not Acceptable Неприемлемо
407 Proxy Authentication Required Необходима аутентификация прокси
408 Request Timeout Истекло время ожидания
409 Conflict Конфликт
410 Gone Удален
411 Length Required Необходима длина
412 Precondition Failed Условие ложно
413 Payload Too Large Большая нагрузка
414 URI Too Long URI слишком длинный
415 Unsupported Media Type Неподдерживаемый тип данных
416 Range Not Satisfiable Неприемлемый диапазон
417 Expectation Failed Ожидаемое неприемлемо
418-420 Не назначены
421 Misdirected Request Неверно адресованный запрос
422 Unprocessable Entity Необрабатываемый экземпляр
423 Locked Заблокировано
424 Failed Dependency Невыполненная зависимость
425 Не назначен
426 Upgrade Required Необходимо обновление
427 Не назначен
428 Precondition Required Необходимо предусловие
429 Too Many Requests Слишком много запросов
430 Не назначен
431 Request Header Fields Too Large Поля заголовка запроса слишком большие
432-450 Не назначены
451 Unavailable for Legal Reasons Недоступно по юридическим причинам
432-499 Не назначены
5xx Server Error Ошибка сервера
500 Internal Server Error Внутренняя ошибка сервера
501 Not Implemented Не реализовано
502 Bad Gateway Плохой, ошибочный шлюз
503 Service Unavailable Сервис недоступен
504 Gateway Timeout Шлюз не отвечает
505 HTTP Version Not Supported Версия HTTP не поддерживается
506 Variant Also Negotiates Вариант тоже проводит согласование
507 Insufficient Storage Переполнение хранилища
508 Loop Detected Обнаружена петля
509 Не назначен
510 Not Extended Не расширено
511 Network Authentication Required Требуется сетевая аутентификация
512-599 Не назначены
  • HTTP
  • Hypertext Transfer Protocol (HTTP) Status Code Registry
  • Список кодов состояния HTTP

31.08.2014

Error: success и что делать по этому поводу +8

Процессоры, Разработка под Linux, Промышленное программирование


Рекомендация: подборка платных и бесплатных курсов PR-менеджеров — https://katalog-kursov.ru/

image

Всем начинающим программистам всегда рассказывают о важности правильного формирования сообщения об ошибках. Всегда говорят о том, что если программа что-то не смогла сделать, то она должна ясно и недвусмысленно рассказать почему это произошло. Рассказывают о важности контроля возвращаемого значения вызываемых функций. При этом даже компиляторы научились выдавать предупреждения если возвращаемое определенными функциями значение игнорируется. Надо сказать что важность обработки ошибок современными программистами принята. Временами это приводит к занятным казусам, как на КДПВ (взята здесь). Мне в реальной жизни несколько раз приходилось сталкиваться с подобными странными диагностическими сообщениями. О последнем случае и методах преодоления такой вот диагностики я и хочу рассказать. Если стало интересно милости прошу под кат. Опытные программисты наверняка не откроют для себя ничего нового, но пофилософствовать о разработке ПО точно смогут.

Для тех, кому «букв много»

  • правильно обрабатывать возвращаемые функцией read() значения важно
  • не забываем проверять актуальность используемого софта (а OpenSource особенно)
  • многофункциональные программы-комбайны в большинстве случаев одинаково неудобны для решения любой из допустимых задач. С другой стороны плодить «москитный флот» не лучшая идея

А вообще у меня грустная новость. Не будет больше никаких картинок. Мы опустимся на уровень системной консоли Linux и будем жить там. При этом будем радоваться. Ибо проект, с которым предстоит работать — достаточно известный загрузчик U-Boot. Проект с открытым исходным кодом, поддерживаемый компанией DENX Software Engineering. Потому порадуемся тому, что у нас есть консоль, есть системное окружение и вообще жизнь кипит. Потому как при работе с этим проектом, как правило, нет ничего подобного — сплошные области памяти, пересылки байт из одного места в другое да ожидание готовности периферии. Но, к частью, эта часть уже выполнена и есть вполне себе рабочий загрузчик для железки. Пора заняться украшениями, которые позволят прикладным программистам как-то влиять на процесс загрузки системы. Ничто не предвещает проблем. Задача давно решена и активно используется таким популярным проектами как OpenWRT и множеством других, чуть менее известных.

Суть очень проста. U-Boot корректирует свое поведение в зависимости от переменных среды. Переменные среды между перезагрузками могут быть сохранены в энергонезависимой памяти. Утилиты командной строки fw_printenv и fw_setenv позволяют соответственно выводить и менять их значение прямо из Linux. Все. В принципе большего и не требуется. Как всегда инструкцию мы будем читать «когда дым рассеется». Да и откуда здесь взяться дыму? Дым весь был выпущен когда загрузчик под эту плату адаптировали. Потому смело набираем команду «fw_printenv», потому как она-то точно ничего сломать не может.

localhost ~ # fw_printenv
Cannot open /dev/mtd1: No such file or directory
localhost ~ # fw_printenv --help
Usage: fw_printenv [OPTIONS]... [VARIABLE]...
Print variables from U-Boot environment

 -h, --help           print this help.
 -v, --version        display version
 -c, --config         configuration file, default:/etc/fw_env.config
 -n, --noheader       do not repeat variable name in output
 -l, --lock           lock node, default:/var/lock

Ну ожидаемо. Конечно. Мы не указали где именно хранятся переменные среды. А «быстрая помощь» однозначно говорит о том, что указать в командной строке и не получится. Надо править конфигурационный файл /etc/fw_env.config. Формат файла довольно простой и интуитивно понятный. Для того, чтоб не создавать самому себе (и окружающим) трудностей я разместил переменные среды U-Boot в самом доступном месте, которое только можно себе придумать. Конкретно в файле uboot.env первого раздела основного накопителя, отформатированного реально переносимой файловой системой vfat (она же FAT-32). И проверил. Из консоли U-Boot переменные сохраняются в файл, при старте из него читаются. Красота. Осталось только дать возможность их править из Linux. Раздел c файлом uboot.env, а еще ядром, файлом дерева устройств, и некоторым дополнительным наполнением критичным для работы системы совершенно логично монтируется к /boot. Потому совершенно не сомневаясь комментирую строчки 11 и 12 (/dev/mtd1 и /dev/mdt2 соответственно) и убираю комментарий со строчки 30 (/boot/uboot.env) в конфигурационном файле.

# VFAT example
/boot/uboot.env 0x0000          0x4000

Все. Вроде все подготовительные операции выполнены. Дубль два.

localhost ~ # fw_printenv
Read error on /boot/uboot.env: Success

Ну здравствуй, КДПВ. Первая разумная мысль, которая посещает любого Linux’оидника в такой ситуации — а что с правами. Впрочем, наш лозунг «Слабоумие и отвага» — мы работаем от root’а. Логично. Чего бояться человеку, который для железки делает загрузчик и имеет самый что ни на есть физический (с паяльником) доступ к плате? А может файла просто нет? Забыл в консоли U-Boot’а сказать «saveenv»? Проверим…

localhost ~ # ls -l /boot/uboot.env
-rwxr-xr-x 1 root root 8192 Dec  2 13:22 /boot/uboot.env

Нет, есть он. И даже читаться может всем миром (ай, как не хорошо). Интересно, а если его не будет?

localhost ~ # mv /boot/uboot.env /boot/uboot.env.bak
localhost ~ # fw_printenv
Cannot open /boot/uboot.env: No such file or directory
localhost ~ # mv /boot/uboot.env.bak /boot/uboot.env

Логично. Тут все правильно. Ладно, тяжело вздохнули и… Это наш кактус, нам его и грызть. Хорошо хоть исходники есть. Надо глянуть, что там у нас происходит. Может мысли какие появятся? Очень быстро находим строку 950 в файле tools/env/fw_env.c:

        lseek(fd, blockstart + block_seek, SEEK_SET);

        rc = read(fd, buf + processed, readlen);
        if (rc == -1) {
            fprintf(stderr, "Read error on %s: %sn",
                DEVNAME(dev), strerror(errno));
            return -1;
        }
        if (rc != readlen) {
            fprintf(stderr,
                "Read error on %s: Attempted to read %zd bytes but got %dn",
                DEVNAME(dev), readlen, rc);
            return -1;
        }

Нет. Тут вполне себе классическая обертка над функцией read(). Практически прямиком из учебника. И, судя по поведению итоговой программы не остается сомнений в том, что read() возвращает -1, но при этом errno остается нулевым. Подождите. Что это за скрежет раздается? А, это мозги шевелиться начали… Хорошо…

Ну что, можно почитать мануал на read? Не, ерунда… Уж вроде про read-то все читано-перечитано. Все мыслимые и немыслимые варианты ошибок с функцией read() давно известны. Не должно быть такого. Что делаем дальше? Правильно, раз исходники не дают ответ — пусть его даст сама система.

localhost ~ # strace fw_printenv
execve("/usr/bin/fw_printenv", ["fw_printenv"], 0x7ebf2400 /* 28 vars */) = 0
brk(NULL)                               = 0x2118000
uname({sysname="Linux", nodename="localhost", ...}) = 0
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=42265, ...}) = 0
mmap2(NULL, 42265, PROT_READ, MAP_PRIVATE, 3, 0) = 0x76f14000
close(3)                                = 0
openat(AT_FDCWD, "/lib/libc.so.6", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
read(3, "177ELF11133(1f~1004"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1286448, ...}) = 0
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x76f12000
mmap2(NULL, 1356160, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x76da1000
mprotect(0x76ed7000, 65536, PROT_NONE)  = 0
mmap2(0x76ee7000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x136000) = 0x76ee7000
mmap2(0x76eea000, 8576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x76eea000
close(3)                                = 0
set_tls(0x76f12ca0)                     = 0
mprotect(0x76ee7000, 8192, PROT_READ)   = 0
mprotect(0x4a9000, 4096, PROT_READ)     = 0
mprotect(0x76f1f000, 4096, PROT_READ)   = 0
munmap(0x76f14000, 42265)               = 0
openat(AT_FDCWD, "/var/lock/fw_printenv.lock", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
flock(3, LOCK_EX)                       = 0
brk(NULL)                               = 0x2118000
brk(0x2139000)                          = 0x2139000
openat(AT_FDCWD, "/etc/fw_env.config", O_RDONLY) = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=1342, ...}) = 0
read(4, "# Configuration file for fw_(pri"..., 4096) = 1342
read(4, "", 4096)                       = 0
close(4)                                = 0
openat(AT_FDCWD, "/boot/uboot.env", O_RDONLY) = 4
fstat64(4, {st_mode=S_IFREG|0755, st_size=8192, ...}) = 0
close(4)                                = 0
openat(AT_FDCWD, "/boot/uboot.env", O_RDONLY) = 4
_llseek(4, 0, [0], SEEK_SET)            = 0
read(4, "n.'202__INF0__=Ravion-V2 I.MX6 CPU"..., 16384) = 8192
write(2, "Read error on /boot/uboot.env: S"..., 39Read error on /boot/uboot.env: Success
) = 39
close(4)                                = 0
flock(3, LOCK_UN)                       = 0
close(3)                                = 0
exit_group(1)                           = ?
+++ exited with 1 +++
localhost ~ #

Люблю Linux. Ай какая красота. Все сразу встало на свои места. Ладно, согласен — не все. Но уже что-то. Самое интересное здесь:

openat(AT_FDCWD, "/boot/uboot.env", O_RDONLY) = 4
_llseek(4, 0, [0], SEEK_SET)            = 0
read(4, "n.'202__INF0__=Ravion-V2 I.MX6 CPU"..., 16384) = 8192
write(2, "Read error on /boot/uboot.env: S"..., 39Read error on /boot/uboot.env: Success
) = 39

Пытаемся прочитать 16384 (16K), а можем только 8192 (8K). В принципе всё. Бинго. Поднимаемся выше и смотрим размер файла. Да, он действительно 8192 байта. Поднимаемся еще выше и смотрим строку в конфиге. Смещение 0, длина 0x4000 или 16384. Исправляем на 0x2000

# VFAT example
/boot/uboot.env 0x0000          0x2000

Да, черт возьми я очень стар. По мне U-Boot’у для переменных среды и килобайта хватит. Еще тут драгоценную память просто так расходовать. Что вы хотите. Коренной житель Питера. Нам с детства вбили что хлеб (ресурсы) надо беречь. А выбрасывать его — это не ценить память погибших из-за его отсутствия в блокаду. И да, мы такие. Спасибо реальным ветеранам, спасибо городским музеям. Которые не смотря ни на что сохраняют память о тех страшных временах. Надеюсь и мои дети этому научатся.

Так вот — о переменных среды для U-Boot. Ну два килобайта. Ну, ладно — четыре. Куда больше? Что там можно писать в таких количествах (и главное зачем)? Потому действительно — был момент когда выделенные по умолчанию 16К урезал до 8. Еще думал — куда столько? Ладно, лирику в сторону — проверяем.

localhost ~ # fw_printenv
__INF0__=Ravion-V2 I.MX6 CPU Module BSP package
__INF1__=Created: Alex A. Mihaylov AKA MinimumLaw, MinimumLaw@Rambler.Ru
[…]
boot_os=1
localhost ~ #

Работает. И даже fw_setenv работает.

localhost ~ # fw_setenv boot_os 0; fw_printenv boot_os
boot_os=0

Можно ставить точку? Думаю нет. Остался один важный вопрос, который уважающий себя программист не имеет права оставить без внимания. Как думаете, какой вопрос?

Правильно думаете. Если посмотреть на код, который лежит в репозитарии U-Boot, то можно легко заметить, что такая ситуация должна корректно отрабатываться. Я выше специально не стал вырезать этот кусок. Больше того, strace совершенно честно и открыто говорит что read возвращает значение 8192. Так почему же мы оказываемся в ветке с ошибкой чтения? Ведь 8192 никак не может равняться -1.

Давайте разбираться. Первая мысль, которая приходит в голову — подождите, но ведь Das U-Boot это динамично развивающийся проект. Может быть мы смотрим репозитарий с последним релизом загрузчика. Но та часть, которую используем мы совсем не обязана быть последней. Она часть пользовательского окружения операционной системы. Это я адаптирую последнюю версию загрузчика, чтоб пульс проекта ощущать. А авторы сборок прикладного ПО скорее за стабильность ратуют. Потому она наверняка последней и не будет. Проверяем.

localhost ~ # fw_printenv --version
Compiled with U-Boot 2019.10
localhost ~ #

Ага! А у меня в работе последняя стабильная (2020.10). Разница в год. Огромная дистанция для динамично развивающегося OpenSource проекта. А давайте посмотрим .

        lseek(fd, blockstart + block_seek, SEEK_SET);

        rc = read(fd, buf + processed, readlen);
        if (rc != readlen) {
            fprintf(stderr, "Read error on %s: %sn",
                DEVNAME(dev), strerror(errno));
            return -1;
        }

Ну да. Так и есть. Уже все исправили. Обидно. Такой красивый баг был. Ладно, на нашу жизнь багов еще припасено. Только успевай разбирать.

Но ведь и это ещё не все. А давайте заглянем в файл «uboot.env»

localhost ~ # hexdump -C /boot/uboot.env
00000000  0a 43 62 eb 5f 5f 49 4e  46 30 5f 5f 3d 52 61 76  |.Cb.__INF0__=Rav|
00000010  69 6f 6e 2d 56 32 20 49  2e 4d 58 36 20 43 50 55  |ion-V2 I.MX6 CPU|
00000020  20 4d 6f 64 75 6c 65 20  42 53 50 20 70 61 63 6b  | Module BSP pack|
00000030  61 67 65 00 5f 5f 49 4e  46 31 5f 5f 3d 43 72 65  |age.__INF1__=Cre|
[...]
00000720  3d 71 70 00 76 65 6e 64  6f 72 3d 72 61 76 69 6f  |=qp.vendor=ravio|
00000730  6e 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |n...............|
00000740  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00002000
localhost ~ #

Вполне очевиден факт — оценка размера блока достаточного для хранения переменных среды, которая была дана мною выше, вполне справедлива. На данный момент использовано 1837 байт (0x7031 – 4) и формат блока довольно простой. Первые 4 байта CRC32, а дальше разделенные нулем переменные в формате переменная=значение. Другими словами поведение утилиты все равно вызывает вопросы. Ну напишет она что размер файла меньше ожидаемого и завершится с ошибкой. Но ведь это не правда. Все значимые и важные данные в него (даже в двухкилобайтный!) вполне бы влезли. Может все же стоит поправить?

Увы нет. И причина этому вполне банальна. Переменные в U-Boot могут храниться в самых разных местах. Файл на vfat разделе это самое приятное место. За это его и выбрал. Но в том же OpenWRT нет таких удобных накопителей. Там SPI-flash. И под переменные среды выделяется целый сектор. Но и тут все может быть не так плохо. Сектор целиком надо стирать. Писать можно частями. Беда с системами, которые используют dataflash или некоторые варианты raw-NAND накопителей. Т.е. с теми системами, которым помимо данных нужна еще и контрольная информация для контроля целостности и исправности. Вот они обязаны писать весь блок целиком.

Получается интересная альтернатива. Или мы для разных накопителей делаем разные утилиты для работы с переменными среды или… Делаем одинаково неудобно для всех. Посмотрите на код утилиты. Посмотрите на формат сохраняемых данных. К сожалению почти всегда так и получается. Хотели, чтоб всем было одинаково хорошо. На выходе всем получилось одинаково плохо. Впрочем, свою функциональность решение вполне обеспечивает. Пусть так и остается.

Вот так неожиданно и получилось легонькое пятничное чтиво. Было бы интересно посмотреть сколько времени суммарно прожила эта ошибка, но… Уже не настолько интересно, чтоб тратить на это время. Как говаривал классик: «Сказка ложь, да в ней намек. Добрым молодцам урок.» Спасибо за то, что дочитали.

P.S.
Пользуясь случаем передаю привет CodeRush Еще раз благодарю за приглашение на Habr. И да, всегда хочется писать о серьезном — о компиляторах, о безопасном программировании непосредственно по железу. А сил хватает только на легкое пятничное чтиво. Ладно, будем считать, что начало положено. Большое путешествие всегда начинается с маленького шага.

На чтение 6 мин. Просмотров 689 Опубликовано 03.09.2019

Если вы получаете коды ошибок « Ошибка 0 (0x0) » или « Ошибка успеха: операция завершена », выполните действия по устранению неполадок, перечисленные в этой статье, чтобы исправить их. их.

Содержание

  1. Error_success – фон и как это исправить
  2. Варианты error_success
  3. Как исправить ошибку «Error_success»

Error_success – фон и как это исправить

Error_success обычно возникает в следующих ситуациях:

  • при обновлении вашего ПК до Windows 10
  • при установке последних обновлений Windows
  • при загрузке приложений из Магазина Windows
  • во время запуска и завершения работы Windows
  • при выполнении различных задач, таких как печать и т. д.

Варианты error_success

В то же время эта ошибка также может возникать в следующих формах:

  • Код ошибки 0x00000000: операция успешно завершена
  • Ошибка успеха: операция успешно завершена
  • Ошибка успеха 0 (0x0)
  • Операция успешно завершена win32exception

Как исправить ошибку «Error_success»

Решение 1. Запустите полное сканирование системы .

Вредоносные программы могут вызвать различные проблемы на вашем компьютере, в том числе ошибки. Выполните полное сканирование системы, чтобы обнаружить любые вредоносные программы, работающие на вашем компьютере. Вы можете использовать встроенные в Windows антивирусные программы, Защитник Windows или сторонние антивирусные решения.

Решение 2. Восстановите реестр .

Самый простой способ восстановить реестр – использовать специальный инструмент, такой как CCleaner. Не забудьте сначала сделать резервную копию реестра, если что-то пойдет не так. Если вы не установили очиститель реестра на свой компьютер, ознакомьтесь с нашей статьей о лучших очистителях реестра для использования на ПК с Windows 10.

Вы также можете использовать средство проверки системных файлов Microsoft для проверки повреждений системных файлов. Однако эта утилита доступна только в Windows 10. Вот как запустить сканирование SFC:

1. Перейдите в Пуск> введите cmd> щелкните правой кнопкой мыши Командная строка> выберите Запуск от имени администратора

2. Теперь введите команду sfc/scannow

3. Дождитесь завершения процесса сканирования и перезагрузите компьютер. Все поврежденные файлы будут заменены при перезагрузке.

Решение 3. Обновите свою ОС .

Убедитесь, что на вашем компьютере установлены последние обновления ОС Windows. В качестве напоминания, Microsoft постоянно выпускает обновления для Windows, чтобы улучшить стабильность системы и устранить различные проблемы. Перейдите в Центр обновления Windows, проверьте наличие обновлений и установите доступные обновления. Чтобы получить доступ к разделу Центра обновления Windows, вы можете просто ввести «обновление» в поле поиска. Этот метод работает на всех версиях Windows.

Если вы ищете конкретное обновление, перейдите на веб-сайт каталога обновлений Microsoft. Просто введите номер КБ соответствующего обновления, нажмите Enter и затем нажмите кнопку загрузки.

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

Решение 4. Обновите драйверы своего ПК

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

Как обновить драйверы в Windows 10

Вы можете исправить наиболее распространенные проблемы с драйверами, установив последние обновления Windows. Просто введите «update» в поле поиска и нажмите «Проверить наличие обновлений», чтобы загрузить и установить последние обновления.

Если вы хотите установить определенные драйверы, запустите диспетчер устройств. Разверните доступные категории и выберите устройство, для которого вы хотите обновить драйвер. Чтобы установить последние обновления драйверов для этого устройства, щелкните его правой кнопкой мыши и выберите «Обновить драйвер».

Третий вариант – загрузить доступные обновления драйверов непосредственно с веб-сайта производителя.

Как обновить драйверы в Windows 7

Вы можете настроить Windows 7 на автоматическую установку последних обновлений драйверов на ваш компьютер.

  1. Перейдите в Пуск> нажмите «Устройства и принтеры».
  2. Щелкните правой кнопкой мыши имя вашего компьютера> выберите Настройки установки устройства.
  3. Нажмите Да, сделайте это автоматически (рекомендуется), а затем нажмите Сохранить изменения. Если вас попросят ввести пароль администратора или подтверждение, введите пароль или предоставьте подтверждение.

Вы также можете использовать Центр обновления Windows для установки на компьютер последних обновлений драйверов. Если Windows не может найти драйвер для вашего устройства, вы можете загрузить доступные обновления драйверов непосредственно с веб-сайта производителя.

Решение 5. Выполните чистую загрузку .

Чистая загрузка запускает Windows, используя минимальный набор драйверов и автозагрузку программ. Это действие устраняет конфликты программного обеспечения и помогает определить, какой конфликт вызывает досадные ошибки error_success .

Как выполнить чистую загрузку в Windows 10 .

  1. Введите Конфигурация системы в поле поиска> нажмите Enter
  2. На вкладке Службы > установите флажок Скрыть все службы Microsoft > и нажмите Отключить все .

3. На вкладке Запуск > нажмите Открыть диспетчер задач .

4. На вкладке Автозагрузка в Диспетчере задач> выберите все элементы> нажмите Отключить .

5. Закройте Диспетчер задач .

6. На вкладке Запуск диалогового окна Конфигурация системы > нажмите ОК > и перезагрузите компьютер.

Как выполнить чистую загрузку в Windows 7 .

1. Перейдите в «Пуск»> введите msconfig.exe> ​​выберите утилиту «Конфигурация системы».
2. На вкладке «Общие» выберите параметр Выборочный запуск> снимите флажок «Загрузить элементы запуска».


3. На вкладке «Службы» установите флажок «Скрыть все службы Microsoft»> нажмите «Отключить все»> нажмите «ОК»> «Перезагрузить».

Для получения дополнительной информации о том, как выполнить чистую загрузку в других версиях Windows, посетите страницу поддержки Microsoft.

Решение 6. Удалите и переустановите приложения и программы, на которые влияет error_success .

Если при использовании определенного приложения или программы возникает ошибка error_success , попробуйте удалить и переустановить соответствующие программы. Кроме того, если эта ошибка возникает вскоре после установки нового приложения или программы, попробуйте удалить соответствующее приложение или программу.

Решение 7. Отключите прокси-сервер .

Этот обходной путь особенно полезен, если вы сталкиваетесь с error_success при загрузке приложения из Магазина Windows или при их обновлении.

1. Перейдите в Поиск> введите Свойства обозревателя> , перейдите на вкладку Подключения >, перейдите в Настройки локальной сети .

2. Снимите флажок с Использовать прокси-сервер для вашей локальной сети> и нажмите Применить .

Хорошей новостью является то, что ошибка «Операция успешно завершена» возникает довольно редко. Если вы столкнулись с другими обходными путями для исправления error_success, не стесняйтесь перечислять шаги по устранению неполадок в разделе комментариев ниже.

Может быть много способов обработки ответов API, поступающих от серверов в Android, но используете ли вы хороший способ обработки? В этой статье мы увидим, как ответы API обрабатываются с помощью Kotlin Sealed Class при использовании библиотеки Retrofit для вызовов API. Этот подход с закрытым классом идеально подходит в этом случае: если пользователь ожидает данные в пользовательском интерфейсе, нам нужно отправить ошибки в наш пользовательский интерфейс, чтобы уведомить пользователя и убедиться, что пользователь не просто увидит пустой экран. или столкнуться с неожиданным пользовательским интерфейсом.

Сделать запечатанный класс

Просто создайте универсальный запечатанный класс с именем Resource в отдельном файле в вашем пакете данных или пакете utils/others. Мы назвали его Resource и сделали его универсальным, потому что мы будем использовать этот класс для переноса наших различных типов ответов API. По сути, нам нужно обновить наш пользовательский интерфейс для этих трех событий, т. е. Success, Error, Loading. Итак, мы создали их как дочерние классы ресурсов для представления различных состояний пользовательского интерфейса. Теперь, в случае успеха, мы получим данные, поэтому обернем их Resource. Успех и в случае ошибки мы завернем сообщение об ошибке в Resource. Ошибка, и в случае загрузки мы просто вернем объект Resource.Loading (вы также можете изменить его, чтобы обернуть данные загрузки в соответствии с вашими потребностями).

Kotlin

sealed class Resource<T>(

    val data: T? = null,

    val message: String? = null

) {

    class Success<T>(data: T) : Resource<T>(data = data)

    class Error<T>(errorMessage: String) : Resource<T>(message = errorMessage)

    class Loading<T> : Resource<T>()

}

Давайте также взглянем на наш интерфейс ApiService (см. чуть ниже), здесь в каждой приостановленной функции вызова API мы оборачиваем наш ответ на вызов API классом Retrofit Response, потому что он предоставляет нам дополнительную информацию о вызовах API. Например: будут ли вызовы успешными или нет, код ошибки и т. д. Мы будем вызывать эти приостановленные функции из наших репозиториев… мы увидим это через какое-то время. Не запутайтесь в нашем пользовательском классе Resource и классе Response Retrofit. Класс Resource просто представляет различные состояния пользовательского интерфейса, в то время как класс Response Retrofit дает нам дополнительную информацию о вызовах API.

Kotlin

interface ExampleApiService {

    @GET("example/popular_articles")

    suspend fun fetchPopularArticles(): Response<PopularArticlesResponse>

    @GET("example/new_articles")

    suspend fun fetchNewArticles(): Response<NewArticlesResponse>

}

Теперь давайте перейдем к основной части обработки успеха/ошибки API.

У вас должны быть разные репозитории в вашем проекте в соответствии с вашими потребностями, и все ваши вызовы API должны происходить через эти репозитории, нам нужно выполнять обработку ошибок каждого вызова API. Итак, нам нужно обернуть каждый вызов API внутри блока try-catch. Но подождите… писать блок try-catch для каждого вызова API — плохая идея. Итак, давайте создадим BaseRepository и напишем там общую функцию приостановки с именем safeApiCall (любое имя, которое вам нравится), которая будет отвечать за обработку ответов API на каждый вызов API.

Kotlin

abstract class BaseRepo() {

    suspend fun <T> safeApiCall(apiToBeCalled: suspend () -> Response<T>): Resource<T> {

        return withContext(Dispatchers.IO) {

            try {

                val response: Response<T> = apiToBeCalled()

                if (response.isSuccessful) {

                    Resource.Success(data = response.body()!!)

                } else {

                    val errorResponse: ExampleErrorResponse? = convertErrorBody(response.errorBody())

                    Resource.Error(errorMessage = errorResponse?.failureMessage ?: "Something went wrong")

                }

            } catch (e: HttpException) {

                Resource.Error(errorMessage = e.message ?: "Something went wrong")

            } catch (e: IOException) {

                Resource.Error("Please check your network connection")

            } catch (e: Exception) {

                Resource.Error(errorMessage = "Something went wrong")

            

        }

    }

    private fun convertErrorBody(errorBody: ResponseBody?): ExampleErrorResponse? {

        return try {

            errorBody?.source()?.let {

                val moshiAdapter = Moshi.Builder().build().adapter(ExampleErrorResponse::class.java)

                moshiAdapter.fromJson(it)

            }

        } catch (exception: Exception) {

            null

        }

    }

}

safeApiCall имеет приостановленную лямбда-функцию с именем «api», которая возвращает данные, завернутые в Resource, чтобы safeApiCall мог получать каждую нашу функцию вызова API. Поскольку safeApiCall является функцией приостановки, мы используем область сопрограммы withContext с Dispatchers.IO, таким образом, все сетевые вызовы будут выполняться в фоновом потоке ввода/вывода. Всякий раз, когда сетевой вызов терпит неудачу, он выдает исключение, поэтому нам нужно вызвать нашу лямбда-функцию «apiToBeCalled» внутри блока try-catch, и здесь, если вызов API будет успешным, мы завернем наши данные об успешном ответе в Resource. Объект Success, и мы вернем его через safeApiCall, и если вызов API завершится неудачно, мы перехватим это конкретное исключение в одном из следующих блоков catch:

  • Когда что-то пойдет не так на сервере при обработке HTTP-запроса, он выдаст исключение HTTP.
  • Когда у пользователя не будет действительного подключения к Интернету/Wi-Fi, он выдаст исключение ввода-вывода Java.
  • Если произойдет какое-либо другое исключение, оно просто появится в общем блоке исключений (поместите этот блок последним). Вы также можете добавить другой блок перехвата исключений в соответствии с вашим конкретным случаем перед последним общим блоком перехвата исключений.

Теперь в блоках catch мы можем просто отправлять собственные сообщения об ошибках или сообщения об ошибках, полученные из исключений в объекте Resource.Error.

Примечание. В случае, если сетевой вызов не завершается ошибкой и вместо этого не выдает исключение, если он отправляет свой собственный ответ json об ошибке (иногда это происходит в случае сбоя аутентификации / истечения срока действия токена / недействительного ключа API / и т. д.), тогда мы выиграли ’ не получите исключение, вместо этого класс Response Retrofit поможет нам определить, возвращая true/false в response.isSuccessful. В приведенном выше коде мы также обрабатываем этот внутренний блок try, если он возвращает false, то внутри другого случая мы будем анализировать собственный пользовательский ответ json об ошибке API (см. Пример ниже) в созданный нами класс pojo, т.е. ExampleErrorResponse. Для разбора этого json-ответа на наш pojo ExampleErrorResponse мы используем библиотеку Moshi.в нашей функции convertErrorBody, которая вернет объект ExampleErrorResponse, а затем мы сможем получить собственный ответ API об ошибке.

{
   “status”: ”UnSuccessful”,
   “failure_message” : ”Invalide api key or api key expires”
}

Итак, в конечном итоге наша функция safeApiCall вернет наш ответ, завернутый в класс Resource. это может быть либо успех, либо ошибка. Если вы поняли до этого, то привет вам. Вы выиграли большую часть битвы.

Теперь давайте изменим способ вызова Apis в ваших текущих репозиториях.

Мы расширим все наши репозитории из BaseRepo, чтобы мы могли получить доступ к функции safeApiCall. И внутри функций нашего репозитория, таких как getPopularArticles, мы передадим нашу функцию ApiService (например: apiService.fetchPopularArticles()) в качестве лямбда-аргумента в safeApiCall, а затем safeApiCall просто обработает свою часть успеха/ошибки, после чего в конечном итоге вернет Resource это дойдет до пользовательского интерфейса через ViewModel.

Примечание. Помните, что когда мы будем вызывать функцию getPopularArticles в модели представления (см. пример ниже), то сначала мы разместим объект Resource.Loading в изменяемых оперативных данных, чтобы наш фрагмент/активность мог наблюдать за этим состоянием загрузки и мог обновить пользовательский интерфейс, чтобы показать загрузку. состояние, а затем, когда мы подготовим наш ответ API, он будет снова опубликован в изменяемых оперативных данных, чтобы пользовательский интерфейс мог снова получить этот ответ (успех/ошибка) и может отображать данные в пользовательском интерфейсе или обновлять себя соответственно.

Kotlin

class ExampleRepo(private val apiService: ExampleApiService) : BaseRepo() {

    suspend fun getPopularArticles() : Resource<PopularArticlesResponse> {

        return safeApiCall { apiService.fetchPopularArticles() }

    }

    suspend fun getPublishedArticles() : Resource<NewlyPublishedArticlesResponse> = safeApiCall { apiService.fetchPublishedArticles() }

}

ViewModel

Kotlin

class ExampleViewModel (private val exampleRepo: ExampleRepo) {

    private val _popularArticles = MutableLiveData<Resource<PopularArticlesResponse>>()

    val popularArticles: LiveData<Resource<PopularArticlesResponse>> = _popularArticles

    init {

        getPopularArticles()

    }

    private fun getPopularArticles() = viewModelScope.launch {

        _popularArticles.postValue(Resource.Loading())

        _popularArticles.postValue(exampleRepo.getPopularArticles())

    }

}

Вот и все, теперь мы можем легко наблюдать все состояния, т.е. Успех, Ошибка, Загрузка в нашем пользовательском интерфейсе. Это было об обработке ошибок API или об обработке ответов API в лучшем виде.

Возможно, вам также будет интересно:

  • Что такое ошибка 0xc0000142 на виндовс 10
  • Что такое ошибка 0xc0000135 windows xp
  • Что такое ошибка 0xc00000e9 и как исправить
  • Что такое ошибка 0xc000007b при запуске игры
  • Что такое ошибка 0xc000007b на 8 windows

  • Понравилась статья? Поделить с друзьями:
    0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии