shmig icon indicating copy to clipboard operation
shmig copied to clipboard

Error /dev/fd/63: No such file or directory (I think process substitution has issues in my target environment)

Open jedwards1211 opened this issue 1 year ago • 8 comments

Something isn't working when trying to run migrations on a wordpress database in SiteGround, but I have no clue how to debug bash... All I see normally is

./shmig: line 548: /dev/fd/63: No such file or directory

I think the read -r on < <("$src" "$steps") inside migrate is failing, but I'm not sure... maybe awk behaves differently on this system?

But ./shmig pending works and prints out the one migration, so I'm not sure why looping over the output of pending_migrations would fail.

If I add set -x I get:

+ ME=shmig
+ CONFIG=./shmig.conf
+ SCHEMA_TABLE=shmig_version
+ CONFIG_EXPLICITLY_SET=0
+ ASK_PASSWORD=0
+ MIGRATIONS=./migrations
++ command -v mysql
+ MYSQL=/bin/mysql
++ command -v psql
+ PSQL=/bin/psql
++ command -v sqlite3
+ SQLITE3=/bin/sqlite3
+ UP_MARK='====  UP  ===='
+ DOWN_MARK='==== DOWN ===='
+ VERSION='Version 1.1.0'
+ COLOR=never
+ RED=
+ CYAN=
+ LRED=
+ LGREEN=
+ LYELLOW=
+ LBLUE=
+ LMAGENTA=
+ LCYAN=
+ CLEAR=
+ BOLD=
+ getopts hc:t:m:d:l:H:p:P:s:a:AVC: arg
+ shift 0
+ [[ -e ./shmig.conf ]]
+ trap 'die "Configuration failed"' ERR
+ . ./shmig.conf
++ TYPE=mysql
++ HOST=localhost
++ DATABASE=tcmacave_wordpress
++ LOGIN=tcmacave_dba
++ PASSWORD=***************
+ trap - ERR
+ LOCAL_CONFIG=./shmig.local.conf
+ '[' -e ./shmig.local.conf ']'
+ [[ 0 -eq 1 ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ case "$COLOR" in
+ COLOR=1
+ [[ 1 -eq 0 ]]
+ [[ -z mysql ]]
+ [[ -z tcmacave_wordpress ]]
+ [[ -d ./migrations ]]
+ [[ 1 -eq 0 ]]
+ ACTION=up
+ shift
++ type -t mysql_check
+ [[ function = \f\u\n\c\t\i\o\n ]]
+ [[ up == \c\r\e\a\t\e ]]
+ mysql_check
+ __generic_checker /bin/mysql 'show tables;' 'create table `shmig_version`(version int not null primary key, migrated_at timestamp not null default current_timestamp);'
+ local exe=/bin/mysql
+ local 'show=show tables;'
+ local 'create=create table `shmig_version`(version int not null primary key, migrated_at timestamp not null default current_timestamp);'
+ [[ -x /bin/mysql ]]
+ TMPF=/tmp/720041406.94452.out
+ rm -f /tmp/720041406.94452.out
+ mysql_cli 'show tables;'
+ local 'args=-N -A -B  tcmacave_wordpress'
+ [[ -n tcmacave_dba ]]
+ args='-u tcmacave_dba    -N -A -B  tcmacave_wordpress'
+ [[ -n localhost ]]
+ args='-h localhost     -u tcmacave_dba    -N -A -B  tcmacave_wordpress'
+ [[ -n '' ]]
+ [[ -n *************** ]]
+ export MYSQL_PWD=***************
+ MYSQL_PWD=***************
+ [[ 1 -ne 0 ]]
+ /bin/mysql -h localhost -u tcmacave_dba -N -A -B tcmacave_wordpress
+ grep -F -qx shmig_version /tmp/720041406.94452.out
+ local rc=0
+ rm -f /tmp/720041406.94452.out
+ '[' 0 -eq 0 ']'
+ true
+ return 0
+ case "$ACTION" in
+ up
+ migrate up 'applying ' '====  UP  ====' '==== DOWN ====' UP pending_migrations mysql_bump_version_sql
+ local me=up
+ local 'message=applying '
+ local 'since=====  UP  ===='
+ local 'till===== DOWN ===='
+ local sname=UP
+ local src=pending_migrations
+ local version_change_sql=mysql_bump_version_sql
+ shift 7
+ local steps=
+ local stopver=
+ [[ 0 -ne 0 ]]
+ is_numeric ''
+ echo ''
+ grep -E -qi '^[0-9]*$'
+ is_numeric ''
+ echo ''
+ grep -E -qi '^[0-9]*$'
+ local fname=
./shmig: line 550: /dev/fd/63: No such file or directory
++ pending_migrations ''
baseos | tcmacaves.org | [email protected]:~/www/tcmacaves.org$ +++ mysql_previous_versions 1
+++ local 'fields=lpad(version, 10, '\''0'\'') as version'
+++ __generic_previous_versions '`' 1 'lpad(version, 10, '\''0'\'') as version'
+++ local 'fields=lpad(version, 10, '\''0'\'') as version'
+++ local 'sql=select lpad(version, 10, '\''0'\'') as version from `shmig_version` order by version desc'
+++ [[ -n 1 ]]
+++ sql='select lpad(version, 10, '\''0'\'') as version from `shmig_version` order by version desc limit 1'
+++ mysql_cli 'select lpad(version, 10, '\''0'\'') as version from `shmig_version` order by version desc limit 1;'
+++ local 'args=-N -A -B  tcmacave_wordpress'
+++ [[ -n tcmacave_dba ]]
+++ args='-u tcmacave_dba    -N -A -B  tcmacave_wordpress'
+++ [[ -n localhost ]]
+++ args='-h localhost     -u tcmacave_dba    -N -A -B  tcmacave_wordpress'
+++ [[ -n '' ]]
+++ [[ -n *************** ]]
+++ export MYSQL_PWD=***************
+++ MYSQL_PWD=***************
+++ [[ 1 -ne 0 ]]
+++ /bin/mysql -h localhost -u tcmacave_dba -N -A -B tcmacave_wordpress
++ local last=
++ last=0
++ find_migrations
++ find -L ./migrations -maxdepth 1 -mindepth 1 -type f
++ sort -t- -k1nr
++ awk -v limit=-1 -v last=0 -F/ 'limit&&$NF~/^[0-9]+-.*\.sql$/&&int($NF)>last{
      print;
      --limit;
      if(!limit)
        exit;
    }'

baseos | tcmacaves.org | [email protected]:~/www/tcmacaves.org$ 
baseos | tcmacaves.org | [email protected]:~/www/tcmacaves.org$ 
baseos | tcmacaves.org | [email protected]:~/www/tcmacaves.org$ ./shmig pending
+ ME=shmig
+ CONFIG=./shmig.conf
+ SCHEMA_TABLE=shmig_version
+ CONFIG_EXPLICITLY_SET=0
+ ASK_PASSWORD=0
+ MIGRATIONS=./migrations
++ command -v mysql
+ MYSQL=/bin/mysql
++ command -v psql
+ PSQL=/bin/psql
++ command -v sqlite3
+ SQLITE3=/bin/sqlite3
+ UP_MARK='====  UP  ===='
+ DOWN_MARK='==== DOWN ===='
+ VERSION='Version 1.1.0'
+ COLOR=never
+ RED=
+ CYAN=
+ LRED=
+ LGREEN=
+ LYELLOW=
+ LBLUE=
+ LMAGENTA=
+ LCYAN=
+ CLEAR=
+ BOLD=
+ getopts hc:t:m:d:l:H:p:P:s:a:AVC: arg
+ shift 0
+ [[ -e ./shmig.conf ]]
+ trap 'die "Configuration failed"' ERR
+ . ./shmig.conf
++ TYPE=mysql
++ HOST=localhost
++ DATABASE=tcmacave_wordpress
++ LOGIN=tcmacave_dba
++ PASSWORD=***************
+ trap - ERR
+ LOCAL_CONFIG=./shmig.local.conf
+ '[' -e ./shmig.local.conf ']'
+ [[ 0 -eq 1 ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ case "$COLOR" in
+ COLOR=1
+ [[ 1 -eq 0 ]]
+ [[ -z mysql ]]
+ [[ -z tcmacave_wordpress ]]
+ [[ -d ./migrations ]]
+ [[ 1 -eq 0 ]]
+ ACTION=pending
+ shift
++ type -t mysql_check
+ [[ function = \f\u\n\c\t\i\o\n ]]
+ [[ pending == \c\r\e\a\t\e ]]
+ mysql_check
+ __generic_checker /bin/mysql 'show tables;' 'create table `shmig_version`(version int not null primary key, migrated_at timestamp not null default current_timestamp);'
+ local exe=/bin/mysql
+ local 'show=show tables;'
+ local 'create=create table `shmig_version`(version int not null primary key, migrated_at timestamp not null default current_timestamp);'
+ [[ -x /bin/mysql ]]
+ TMPF=/tmp/890132899.95221.out
+ rm -f /tmp/890132899.95221.out
+ mysql_cli 'show tables;'
+ local 'args=-N -A -B  tcmacave_wordpress'
+ [[ -n tcmacave_dba ]]
+ args='-u tcmacave_dba    -N -A -B  tcmacave_wordpress'
+ [[ -n localhost ]]
+ args='-h localhost     -u tcmacave_dba    -N -A -B  tcmacave_wordpress'
+ [[ -n '' ]]
+ [[ -n *************** ]]
+ export MYSQL_PWD=***************
+ MYSQL_PWD=***************
+ [[ 1 -ne 0 ]]
+ /bin/mysql -h localhost -u tcmacave_dba -N -A -B tcmacave_wordpress
+ grep -F -qx shmig_version /tmp/890132899.95221.out
+ local rc=0
+ rm -f /tmp/890132899.95221.out
+ '[' 0 -eq 0 ']'
+ true
+ return 0
+ case "$ACTION" in
+ pending
+ [[ 0 -ne 0 ]]
+ local fname=
+ pending_migrations
+ read -r fname
++ mysql_previous_versions 1
++ local 'fields=lpad(version, 10, '\''0'\'') as version'
++ __generic_previous_versions '`' 1 'lpad(version, 10, '\''0'\'') as version'
++ local 'fields=lpad(version, 10, '\''0'\'') as version'
++ local 'sql=select lpad(version, 10, '\''0'\'') as version from `shmig_version` order by version desc'
++ [[ -n 1 ]]
++ sql='select lpad(version, 10, '\''0'\'') as version from `shmig_version` order by version desc limit 1'
++ mysql_cli 'select lpad(version, 10, '\''0'\'') as version from `shmig_version` order by version desc limit 1;'
++ local 'args=-N -A -B  tcmacave_wordpress'
++ [[ -n tcmacave_dba ]]
++ args='-u tcmacave_dba    -N -A -B  tcmacave_wordpress'
++ [[ -n localhost ]]
++ args='-h localhost     -u tcmacave_dba    -N -A -B  tcmacave_wordpress'
++ [[ -n '' ]]
++ [[ -n *************** ]]
++ export MYSQL_PWD=***************
++ MYSQL_PWD=***************
++ [[ 1 -ne 0 ]]
++ /bin/mysql -h localhost -u tcmacave_dba -N -A -B tcmacave_wordpress
+ local last=
+ last=0
+ find_migrations
+ find -L ./migrations -maxdepth 1 -mindepth 1 -type f
+ sort -t- -k1nr
+ awk -v limit=-1 -v last=0 -F/ 'limit&&$NF~/^[0-9]+-.*\.sql$/&&int($NF)>last{
      print;
      --limit;
      if(!limit)
        exit;
    }'
+ echo 1728249840-recreate-views-and-procs.sql
1728249840-recreate-views-and-procs.sql
+ read -r fname
baseos | tcmacaves.org | [email protected]:~/www/tcmacaves.org$ ./shmig up
+ ME=shmig
+ CONFIG=./shmig.conf
+ SCHEMA_TABLE=shmig_version
+ CONFIG_EXPLICITLY_SET=0
+ ASK_PASSWORD=0
+ MIGRATIONS=./migrations
++ command -v mysql
+ MYSQL=/bin/mysql
++ command -v psql
+ PSQL=/bin/psql
++ command -v sqlite3
+ SQLITE3=/bin/sqlite3
+ UP_MARK='====  UP  ===='
+ DOWN_MARK='==== DOWN ===='
+ VERSION='Version 1.1.0'
+ COLOR=never
+ RED=
+ CYAN=
+ LRED=
+ LGREEN=
+ LYELLOW=
+ LBLUE=
+ LMAGENTA=
+ LCYAN=
+ CLEAR=
+ BOLD=
+ getopts hc:t:m:d:l:H:p:P:s:a:AVC: arg
+ shift 0
+ [[ -e ./shmig.conf ]]
+ trap 'die "Configuration failed"' ERR
+ . ./shmig.conf
++ TYPE=mysql
++ HOST=localhost
++ DATABASE=tcmacave_wordpress
++ LOGIN=tcmacave_dba
++ PASSWORD=***************
+ trap - ERR
+ LOCAL_CONFIG=./shmig.local.conf
+ '[' -e ./shmig.local.conf ']'
+ [[ 0 -eq 1 ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ case "$COLOR" in
+ COLOR=1
+ [[ 1 -eq 0 ]]
+ [[ -z mysql ]]
+ [[ -z tcmacave_wordpress ]]
+ [[ -d ./migrations ]]
+ [[ 1 -eq 0 ]]
+ ACTION=up
+ shift
++ type -t mysql_check
+ [[ function = \f\u\n\c\t\i\o\n ]]
+ [[ up == \c\r\e\a\t\e ]]
+ mysql_check
+ __generic_checker /bin/mysql 'show tables;' 'create table `shmig_version`(version int not null primary key, migrated_at timestamp not null default current_timestamp);'
+ local exe=/bin/mysql
+ local 'show=show tables;'
+ local 'create=create table `shmig_version`(version int not null primary key, migrated_at timestamp not null default current_timestamp);'
+ [[ -x /bin/mysql ]]
+ TMPF=/tmp/368124707.95851.out
+ rm -f /tmp/368124707.95851.out
+ mysql_cli 'show tables;'
+ local 'args=-N -A -B  tcmacave_wordpress'
+ [[ -n tcmacave_dba ]]
+ args='-u tcmacave_dba    -N -A -B  tcmacave_wordpress'
+ [[ -n localhost ]]
+ args='-h localhost     -u tcmacave_dba    -N -A -B  tcmacave_wordpress'
+ [[ -n '' ]]
+ [[ -n *************** ]]
+ export MYSQL_PWD=***************
+ MYSQL_PWD=***************
+ [[ 1 -ne 0 ]]
+ /bin/mysql -h localhost -u tcmacave_dba -N -A -B tcmacave_wordpress
+ grep -F -qx shmig_version /tmp/368124707.95851.out
+ local rc=0
+ rm -f /tmp/368124707.95851.out
+ '[' 0 -eq 0 ']'
+ true
+ return 0
+ case "$ACTION" in
+ up
+ migrate up 'applying ' '====  UP  ====' '==== DOWN ====' UP pending_migrations mysql_bump_version_sql
+ local me=up
+ local 'message=applying '
+ local 'since=====  UP  ===='
+ local 'till===== DOWN ===='
+ local sname=UP
+ local src=pending_migrations
+ local version_change_sql=mysql_bump_version_sql
+ shift 7
+ local steps=
+ local stopver=
+ [[ 0 -ne 0 ]]
+ is_numeric ''
+ echo ''
+ grep -E -qi '^[0-9]*$'
+ is_numeric ''
+ echo ''
+ grep -E -qi '^[0-9]*$'
+ local fname=
./shmig: line 550: /dev/fd/63: No such file or directory
++ pending_migrations ''
+++ mysql_previous_versions 1
baseos | tcmacaves.org | [email protected]:~/www/tcmacaves.org$ +++ local 'fields=lpad(version, 10, '\''0'\'') as version'
+++ __generic_previous_versions '`' 1 'lpad(version, 10, '\''0'\'') as version'
+++ local 'fields=lpad(version, 10, '\''0'\'') as version'
+++ local 'sql=select lpad(version, 10, '\''0'\'') as version from `shmig_version` order by version desc'
+++ [[ -n 1 ]]
+++ sql='select lpad(version, 10, '\''0'\'') as version from `shmig_version` order by version desc limit 1'
+++ mysql_cli 'select lpad(version, 10, '\''0'\'') as version from `shmig_version` order by version desc limit 1;'
+++ local 'args=-N -A -B  tcmacave_wordpress'
+++ [[ -n tcmacave_dba ]]
+++ args='-u tcmacave_dba    -N -A -B  tcmacave_wordpress'
+++ [[ -n localhost ]]
+++ args='-h localhost     -u tcmacave_dba    -N -A -B  tcmacave_wordpress'
+++ [[ -n '' ]]
+++ [[ -n *************** ]]
+++ export MYSQL_PWD=***************
+++ MYSQL_PWD=***************
+++ [[ 1 -ne 0 ]]
+++ /bin/mysql -h localhost -u tcmacave_dba -N -A -B tcmacave_wordpress
++ local last=
++ last=0
++ find_migrations
++ find -L ./migrations -maxdepth 1 -mindepth 1 -type f
++ sort -t- -k1nr
++ awk -v limit=-1 -v last=0 -F/ 'limit&&$NF~/^[0-9]+-.*\.sql$/&&int($NF)>last{
      print;
      --limit;
      if(!limit)
        exit;
    }'

Output of uname -a:

Linux gcam1190.siteground.biz 6.6.41-MCIclouder486-c9 #100486 SMP PREEMPT_DYNAMIC Mon Jul 22 12:20:50 EEST 2024 x86_64 GNU/Linux

Bash is GNU bash, version 5.2.26(1)-release (x86_64-redhat-linux-gnu)

jedwards1211 avatar Oct 06 '24 22:10 jedwards1211

I would run shmig remotely, but I basically have to run it on the host to work around MySQL's dumb permissions model...dumping the SQL for views and shared procedures doesn't work unless I'm connected from the same IP that created them, and I'm unable to grant my db user permissions to see that regardless of connection IP on this shared host (the fact that MySQL permissions are structured this way is so terrible).

jedwards1211 avatar Oct 06 '24 22:10 jedwards1211

I'm guessing that somehow I don't have sufficient permissions on this shared host to be able to do process substitution. But I bet command substitution would work for me. You must be using process substitution in case the list of migrations is huge, right?

jedwards1211 avatar Oct 07 '24 00:10 jedwards1211

thanks for all the details and a patch.

i’ll take a look this weekend.

mbucc avatar Oct 07 '24 23:10 mbucc

I see SiteGround uses chroot. I guess their chroot config does not allow access to files under /dev.

mbucc avatar Oct 19 '24 00:10 mbucc

Per note on PR, the only potential issue I see is with the change in error handling.

The second change in the PR includes a change to loop that already uses PIPESTATUS inside it. Since the patch now uses pipe instead of process substitution, I want to be sure that doesn't change the PIPESTATUS error handling logic inside that loop.

mbucc avatar Oct 19 '24 00:10 mbucc

I guess their chroot config does not allow access to files under /dev.

Yeah probably so. Makes me wish there were a way to control the location of the file used for streaming the output in process substitution

jedwards1211 avatar Oct 21 '24 21:10 jedwards1211

What happens if you run this in your account:

echo abc > /dev/fd/1

(/dev/fd/1 is stdout of the current process.)

mbucc avatar Oct 21 '24 23:10 mbucc

Yep I get -bash: /dev/fd/1: No such file or directory. This is shared hosting so I'm definitely not surprised that it's pretty locked down.

As a side note, I tried tunneling the MySQL connection through SSH so I could run shmig remotely and still be applying migrations as my MySQL user @localhost. Siteground doesn't allow SSH port forwarding, but I was able to use this funky workaround with bash's builtin TCP redirection. Unfortunately it was much slower though, so I'm still SSHing in and running shmig with this patch applied.

jedwards1211 avatar Oct 22 '24 19:10 jedwards1211