diff options
author | Aaron Plattner <aplattner@nvidia.com> | 2008-02-13 10:20:36 -0800 |
---|---|---|
committer | Aaron Plattner <aplattner@nvidia.com> | 2008-02-13 10:20:36 -0800 |
commit | 6d2a0069d419975ba01c2c423b18ef7cd2e76a6f (patch) | |
tree | 130e177fc9fa77d4c7d0788ba07c3b5af42dd84f |
1.0-61061.0-6106
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | ChangeLog | 92 | ||||
-rw-r--r-- | Makefile | 256 | ||||
-rw-r--r-- | README | 106 | ||||
-rw-r--r-- | backup.c | 1317 | ||||
-rw-r--r-- | backup.h | 48 | ||||
-rw-r--r-- | command-list.c | 714 | ||||
-rw-r--r-- | command-list.h | 90 | ||||
-rw-r--r-- | crc.c | 116 | ||||
-rw-r--r-- | crc.h | 30 | ||||
-rw-r--r-- | files.c | 1481 | ||||
-rw-r--r-- | files.h | 61 | ||||
-rw-r--r-- | format.c | 179 | ||||
-rw-r--r-- | format.h | 43 | ||||
-rw-r--r-- | gen-ui-array.c | 94 | ||||
-rw-r--r-- | install-from-cwd.c | 653 | ||||
-rw-r--r-- | kernel.c | 1568 | ||||
-rw-r--r-- | kernel.h | 43 | ||||
-rw-r--r-- | log.c | 168 | ||||
-rw-r--r-- | misc.c | 1405 | ||||
-rw-r--r-- | misc.h | 68 | ||||
-rw-r--r-- | mkprecompiled.c | 744 | ||||
-rw-r--r-- | ncurses-ui.c | 2139 | ||||
-rw-r--r-- | nvidia-installer-ui.h | 155 | ||||
-rw-r--r-- | nvidia-installer.c | 770 | ||||
-rw-r--r-- | nvidia-installer.h | 321 | ||||
-rw-r--r-- | precompiled.c | 341 | ||||
-rw-r--r-- | precompiled.h | 56 | ||||
-rw-r--r-- | sanity.c | 305 | ||||
-rw-r--r-- | sanity.h | 34 | ||||
-rw-r--r-- | snarf-ftp.c | 415 | ||||
-rw-r--r-- | snarf-http.c | 489 | ||||
-rw-r--r-- | snarf-internal.h | 87 | ||||
-rw-r--r-- | snarf.c | 536 | ||||
-rw-r--r-- | snarf.h | 47 | ||||
-rw-r--r-- | stream-ui.c | 467 | ||||
-rw-r--r-- | tls_test.c | 47 | ||||
-rwxr-xr-x | tls_test_Linux-ia64 | bin | 0 -> 14162 bytes | |||
-rwxr-xr-x | tls_test_Linux-x86 | bin | 0 -> 3220 bytes | |||
-rwxr-xr-x | tls_test_Linux-x86_64 | bin | 0 -> 5064 bytes | |||
-rw-r--r-- | tls_test_dso.c | 7 | ||||
-rwxr-xr-x | tls_test_dso_Linux-ia64.so | bin | 0 -> 10260 bytes | |||
-rwxr-xr-x | tls_test_dso_Linux-x86.so | bin | 0 -> 3384 bytes | |||
-rwxr-xr-x | tls_test_dso_Linux-x86_64.so | bin | 0 -> 4920 bytes | |||
-rw-r--r-- | update.c | 303 | ||||
-rw-r--r-- | update.h | 40 | ||||
-rw-r--r-- | user-interface.c | 642 | ||||
-rw-r--r-- | user-interface.h | 51 |
48 files changed, 16868 insertions, 0 deletions
@@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..5f5aaf7 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,92 @@ + +2004-01-12 version 1.0.6 + + * Add support for installing on Linux 2.6 kernels. + + * Deprecate the "--kernel-include-path" commandline option, + in favor of "--kernel-source-path". + + * Added "--no-rpms" option to disable the check for conflicting rpms. + +2003-12-16 version 1.0.5 + + * Have nvidia-installer perforce a gcc version check itself (ie: + check that the current compiler and the compiler used to compile + the kernel are a close enough match to build the NVIDIA kernel module. + + * Remove conflicting libraries in /usr/X11R6/lib/tls/ + + * Add new option "--no-abi-note". + +2003-09-23 version 1.0.4 + + * When searching for a precompiled kernel interface, also look in + /lib/modules/precompiled/`uname -r`/nvidia/gfx/, and in + any directory specified by the new commandline option + "--precompiled-kernel-interfaces-path". + + * When searching for a precompiled kernel interface, ensure that + the version number stamped in the precompiled kernel interface + matches that of the driver we are installing. + + * Add tls_test binaries for IA64 + + * Rewrite the tls_test to more closely mimic the tls usage + pattern of the OpenGL driver: dlopen() a dso that uses the + local-exec access model to access a thread local variable. + +2003-08-13 version 1.0.3 + + * Add new commandline option "--kernel-module-only" to allow + users to install additional kernel modules on top of an + existing installation. + + * Add new commandline options "--no-network" and "--no-backup". + + * Warn users if they are installing while in runlevel 1. + + * Install a libGL.la file. + + * Fix SEGV triggered when the signal handler called ui->close(). + + * When the user does not accept the license agreement, + print "License not accepted. Aborting installation." with + ui_message(), rather than ui_log() so that the user gets this + feedback displayed on screen, rather than just printed to the + log file. + +2003-06-24 version 1.0.2 + + * Add new .run/installer option "--add-this-kernel"; when this option + is specified, the installer will build a precompiled kernel + interface for the running kernel, and the .run file will be + repackaged to include this new precompiled kernel interface. + + * If we are building for a non-running kernel, specify that + kernel name on the depmod commandline line (see depmod(8) + manpage for details). Patch provided by Nigel Spowage + <Nigel.Spowage@energis.com> + + * Only execute `/usr/bin/chrc.config` if the distro is SuSE or + UnitedLinux *and* the file exists. Patch from Stefan Dirsch + <sndirsch@suse.de> + +2003-04-24 version 1.0.1 + + * Do not install the NVIDIA OpenGL header files by default; + they will be installed in the documentation directory, + and can be optionally installed on the system with the + "--opengl-headers" commandline option. + + * Abort installation if any X server is currently running + (even if it is not using an NVIDIA driver). We check if an + X server is running by checking for existence of the files + /tmp/.X[0-7]-lock. + + * Add a signal handler to tls_test to avoid dumping core files, + and prevent error messages from being printed to stdout. + +2003-03-31 version 1.0 + + * Initial Release. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..12ee6cf --- /dev/null +++ b/Makefile @@ -0,0 +1,256 @@ +# +# nvidia-installer: A tool for installing NVIDIA software packages on +# Unix and Linux systems. +# +# Copyright (C) 2003 NVIDIA Corporation +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the: +# +# Free Software Foundation, Inc. +# 59 Temple Place - Suite 330 +# Boston, MA 02111-1307, USA +# +# +# Makefile +# + +# default definitions; can be overwridden by users +ifndef CC + CC = gcc +endif + +ifndef LD + LD = ld +endif + +ifndef HOST_CC + HOST_CC = $(CC) +endif + +ifndef HOST_LD + HOST_LD = $(LD) +endif + + +ifndef CFLAGS + CFLAGS = -g -O -Wall +endif + +SHELL = /bin/sh +INSTALL = install -m 755 + +ifeq ($(NVDEBUG),1) + STRIP = true +else + ifndef STRIP + STRIP = strip + endif +endif + + +# default prefix +ifdef ROOT + prefix = $(ROOT)/usr +else + prefix = /usr/local +endif + +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin + +# Can be overwitten by users for cross-compiling +# get the os and architecture +ifndef INSTALLER_OS + INSTALLER_OS := $(shell uname) +endif + +ifndef INSTALLER_ARCH + INSTALLER_ARCH := $(shell uname -m) +endif + +# cook the architecture +INSTALLER_ARCH := $(subst i386,x86,$(INSTALLER_ARCH)) +INSTALLER_ARCH := $(subst i486,x86,$(INSTALLER_ARCH)) +INSTALLER_ARCH := $(subst i586,x86,$(INSTALLER_ARCH)) +INSTALLER_ARCH := $(subst i686,x86,$(INSTALLER_ARCH)) + + +NVIDIA_INSTALLER = nvidia-installer +MKPRECOMPILED = mkprecompiled + +NVIDIA_INSTALLER_PROGRAM_NAME = "nvidia-installer" +NVIDIA_INSTALLER_VERSION = "1.0.6" + +NCURSES_UI = nvidia-installer-ncurses-ui.so +NCURSES_UI_C = g_$(NCURSES_UI:.so=.c) + +TLS_TEST_C = g_tls_test.c +TLS_TEST_DSO_C = g_tls_test_dso.c +TLS_TEST = tls_test_$(INSTALLER_OS)-$(INSTALLER_ARCH) +TLS_TEST_DSO_SO = tls_test_dso_$(INSTALLER_OS)-$(INSTALLER_ARCH).so + +TLS_TEST_32_C = g_tls_test_32.c +TLS_TEST_DSO_32_C = g_tls_test_dso_32.c +TLS_TEST_32 = tls_test_Linux-x86 +TLS_TEST_DSO_SO_32 = tls_test_dso_Linux-x86.so + +GEN_UI_ARRAY = ./gen-ui-array +CONFIG_H = config.h +STAMP_C = g_stamp.c + +# Setup some architecture specific build options +ifeq ($(INSTALLER_OS)-$(INSTALLER_ARCH), Linux-x86_64) +TLS_MODEL=initial-exec +PIC=-fPIC +CFLAGS += -DNV_X86_64 +# Only Linux-x86_64 needs the tls_test_32 files +COMPAT_32_SRC = $(TLS_TEST_32_C) $(TLS_TEST_DSO_32_C) +else +# So far all other platforms use local-exec +TLS_MODEL=local-exec +PIC= +# Non-Linux-x86_64 platforms do not include the tls_test_32 files +COMPAT_32_SRC = +endif + +SRC = backup.c \ + command-list.c \ + crc.c \ + files.c \ + format.c \ + install-from-cwd.c \ + kernel.c \ + log.c \ + misc.c \ + nvidia-installer.c \ + precompiled.c \ + snarf-ftp.c \ + snarf-http.c \ + snarf.c \ + stream-ui.c \ + update.c \ + user-interface.c \ + sanity.c + +ALL_SRC = $(SRC) $(NCURSES_UI_C) $(TLS_TEST_C) $(TLS_TEST_DSO_C) \ + $(COMPAT_32_SRC) $(STAMP_C) + +OBJS = $(ALL_SRC:.c=.o) + +ALL_CFLAGS = -I. $(CFLAGS) -imacros $(CONFIG_H) +ALL_LDFLAGS = -ldl $(LDFLAGS) + +MKPRECOMPILED_SRC = crc.c mkprecompiled.c +MKPRECOMPILED_OBJS = $(MKPRECOMPILED_SRC:.c=.o) + +# and now, the build rules: + +default: all + +all: $(NVIDIA_INSTALLER) $(MKPRECOMPILED) + +install: NVIDIA_INSTALLER_install MKPRECOMPILED_install + +NVIDIA_INSTALLER_install: $(NVIDIA_INSTALLER) + $(STRIP) $< + $(INSTALL) $< $(bindir)/$< + +MKPRECOMPILED_install: $(MKPRECOMPILED) + $(INSTALL) $< $(bindir)/$< + +$(MKPRECOMPILED): $(CONFIG_H) $(MKPRECOMPILED_OBJS) + $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(MKPRECOMPILED_OBJS) -o $@ + +$(NVIDIA_INSTALLER): $(CONFIG_H) $(OBJS) + $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(OBJS) -o $@ + +$(NCURSES_UI_C): $(GEN_UI_ARRAY) $(NCURSES_UI) + $(GEN_UI_ARRAY) $(NCURSES_UI) ncurses_ui_array > $@ + +$(GEN_UI_ARRAY): gen-ui-array.c $(CONFIG_H) + $(HOST_CC) $(ALL_CFLAGS) $< -o $@ + +$(NCURSES_UI): ncurses-ui.o + $(CC) -o $@ -shared ncurses-ui.o -lncurses + +$(TLS_TEST_C): $(GEN_UI_ARRAY) $(TLS_TEST) + $(GEN_UI_ARRAY) $(TLS_TEST) tls_test_array > $@ + +$(TLS_TEST_DSO_C): $(GEN_UI_ARRAY) $(TLS_TEST_DSO_SO) + $(GEN_UI_ARRAY) $(TLS_TEST_DSO_SO) tls_test_dso_array > $@ + +$(TLS_TEST_32_C): $(GEN_UI_ARRAY) $(TLS_TEST_32) + $(GEN_UI_ARRAY) $(TLS_TEST_32) tls_test_array_32 > $@ + +$(TLS_TEST_DSO_32_C): $(GEN_UI_ARRAY) $(TLS_TEST_DSO_SO_32) + $(GEN_UI_ARRAY) $(TLS_TEST_DSO_SO_32) tls_test_dso_array_32 > $@ + +ncurses-ui.o: ncurses-ui.c $(CONFIG_H) + $(CC) -c $(ALL_CFLAGS) $< -fPIC -o $@ + +%.o: %.c $(CONFIG_H) + $(CC) -c $(ALL_CFLAGS) $< -o $@ + +%.d: %.c + @set -e; $(CC) -MM $(CPPFLAGS) $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ + [ -s $@ ] || rm -f $@ + +$(CONFIG_H): + @ rm -f $@ + @ echo "#define INSTALLER_OS \"$(INSTALLER_OS)\"" >> $@ + @ echo "#define INSTALLER_ARCH \"$(INSTALLER_ARCH)\"" >> $@ + @ echo -n "#define NVIDIA_INSTALLER_VERSION " >> $@ + @ echo "\"$(NVIDIA_INSTALLER_VERSION)\"" >> $@ + @ echo -n "#define PROGRAM_NAME " >> $@ + @ echo "\"$(NVIDIA_INSTALLER_PROGRAM_NAME)\"" >> $@ + +$(STAMP_C): $(filter-out $(STAMP_C:.c=.o), $(OBJS)) + @ rm -f $@ + @ echo -n "const char NV_ID[] = \"nvidia id: " >> $@ + @ echo -n "$(NVIDIA_INSTALLER_PROGRAM_NAME): " >> $@ + @ echo -n "version $(NVIDIA_INSTALLER_VERSION) " >> $@ + @ echo -n "($(shell whoami)@$(shell hostname)) " >> $@ + @ echo "$(shell date)\";" >> $@ + @ echo "const char *pNV_ID = NV_ID + 11;" >> $@ + +clean clobber: + rm -rf $(NVIDIA_INSTALLER) $(MKPRECOMPILED) \ + $(NCURSES_UI) $(NCURSES_UI_C) \ + $(TLS_TEST_C) $(TLS_TEST_DSO_C) $(COMPAT_32_SRC) \ + $(GEN_UI_ARRAY) $(CONFIG_H) $(STAMP_C) *.o *~ *.d + +# rule to rebuild tls_test and tls_test_dso; a precompiled tls_test +# and tls_test_dso is distributed with nvidia_installer because they +# require a recent toolchain to build. + +rebuild_tls_test: tls_test.c + gcc -Wall -O2 -fomit-frame-pointer -o $(TLS_TEST) -ldl $< + strip $(TLS_TEST) + +rebuild_tls_test_dso: tls_test_dso.c + gcc -Wall -O2 $(PIC) -fomit-frame-pointer -c $< -ftls-model=$(TLS_MODEL) + gcc -o $(TLS_TEST_DSO_SO) -shared tls_test_dso.o + strip $(TLS_TEST_DSO_SO) + +# dummy rule to override implicit rule that builds tls_test from +# tls_test.c + +tls_test: tls_test.c + touch $@ + +print_version: + @ echo $(NVIDIA_INSTALLER_VERSION) + +-include $(SRC:.c=.d) @@ -0,0 +1,106 @@ + +__________________________________________________________________________ + +NVIDIA-INSTALLER SOURCE DOCUMENTATION +__________________________________________________________________________ + +Information on how to use the nvidia-installer is in: + + (sec-02) INSTALLING THE NVIDIA DRIVER + +of the NVIDIA driver README (available from the NVIDIA Linux driver +download page, and installed in /usr/share/doc/NVIDIA_GLX-1.0/). + + +There is not currently any formal documentation describing the +implementation of nvidia-installer, but the source code is fairly +well commented. + + +One interesting thing to note is that user interface shared libraries +are "built into" nvidia-installer to avoid potential problems with +the installer not being able to find the user interface libraries (or +finding the wrong ones, etc): after the shared lib is built, the utility +`gen-ui-array` is run on it to create a source file storing a byte +array of the data that is the shared library. When the installer runs, +it writes this byte data to a temporary file and then dlopens() it. + + +This directory also contains source for a simple tool 'mkprecompiled', +which is used for stamping nv-linux.o's (aka the kernel interfaces) +with the necessary information so that the installer can match it to a +running kernel. + +To build a precompiled kernel interface, you might do the following: + + sh NVIDIA-Linux-x86-1.0-XXXX.run --extract-only + cd NVIDIA-Linux-x86-1.0-XXXX/usr/src/nv/ + make nv-linux.o + mkprecompiled --interface=nv-linux.o --output=nv-linux.o-mykernel \ + --description="This is not an interesting description" \ + --proc-version="`cat /proc/version`" \ + --major=1 --minor=0 --patch=XXXX + mv nv-linux.o-mykernel precompiled/ + +(where "XXXX" is replaced with the driver version number). + + +To build a precompiled kernel interface for a kernel other than +the currently running one, you can build nv-linux.o with: + + make nv-linux.o SYSSRC=/path/to/kernel-source + +and extract the proc version string from a compressed kernel image with: + + gzip -dc /boot/vmlinux.gz | strings | grep "^Linux version" + + + +nvidia-installer will scan the contents of the usr/src/nv/precompiled/ +directory for any precompiled kernel interfaces that match the running +kernel's /proc/version string. + +If you would like to provide precompiled kernel interfaces for others +to use, you may build them as above. To use them, users can do the +following: + + sh NVIDIA-Linux-x86-1.0-XXXX.run --extract-only + cp nv-linux.o-mykernel NVIDIA-Linux-x86-1.0-XXXX/usr/src/nv/ + ./NVIDIA-Linux-x86-1.0-XXXX/nvidia-installer + + +(Updated: 2003-09-23) The search path for directories containing +precompiled kernel interfaces has been extended; the heuristic +is now: + + - if --precompiled-kernel-interfaces-path was specified, search + in that directory; if no match found, then + + - search in the directory /lib/modules/precompiled/`uname -r`/nvidia/gfx/, + if no match found, then + + - search in the usr/src/nv/precompiled directory of the .run file, + if no match found, then + + - search on the ftp site, if no match found, then + + - give up and just build the kernel module yourself + + +TODO: + +- Edit/generate an XF86Config file + +- Add new user interfaces (gtk+/qt/your toolkit of choice). + +- Add additional tests to be run for the '--sanity' option. + +- Cleanup memory leaks. + +- Improve error messages. + +- Internationalization. + +Patches are very welcome, and may be submitted to linux-bugs@nvidia.com + + diff --git a/backup.c b/backup.c new file mode 100644 index 0000000..9841541 --- /dev/null +++ b/backup.c @@ -0,0 +1,1317 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * backup.c - this source file contains functions used for backing up + * (and restoring) files that need to be moved out of the way during + * installation. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/mman.h> +#include <ctype.h> +#include <stdlib.h> + +#include "nvidia-installer.h" +#include "user-interface.h" +#include "backup.h" +#include "files.h" +#include "crc.h" +#include "misc.h" + +#define BACKUP_DIRECTORY "/var/lib/nvidia" +#define BACKUP_LOG (BACKUP_DIRECTORY "/log") + +/* + * XXX when uninstalling should we remove directories that were + * created by our installation? + */ + + + + + +/* + * Syntax for the backup log file: + * + * 1. The first line is the version string, assumed to be in the form: + * MAJOR.MINOR-PATCH + * + * 2. The second line is the driver description. + * + * XXX do we need anything to distinguish between official builds, + * nightlies, etc...? + * + * 3. The rest of the file is file entries; a file entry can be any one of: + * + * INSTALLED_FILE: <filename> + * + * INSTALLED_SYMLINK: <filename> + * <target> + * + * BACKED_UP_SYMLINK: <filename> + * <target> + * <permissions> <uid> <gid> + * + * BACKED_UP_FILE_NUM: <filename> + * <filesize> <permissions> <uid> <gid> + * + */ + +#define BACKUP_LOG_PERMS (S_IRUSR|S_IWUSR) + +#define BACKUP_DIRECTORY_PERMS (S_IRUSR|S_IWUSR|S_IXUSR) + + +/* + * uninstall struct + * + */ + +typedef struct { + + int num; + char *filename; + char *target; + uint32 crc; + mode_t mode; + uid_t uid; + gid_t gid; + int ok; + +} BackupLogEntry; + + +typedef struct { + char *version; + char *description; + BackupLogEntry *e; + int n; +} BackupInfo; + + + +static BackupInfo *read_backup_log_file(Options *op); + +static int check_backup_log_entries(Options *op, BackupInfo *b); + +static int do_uninstall(Options *op); + +static int sanity_check_backup_log_entries(Options *op, BackupInfo *b); + + + + + + + + +/* + * init_backup() - initialize the backup engine; this consists of + * creating a new backup directory, and writing to the log file that + * we're about to install a new driver version. + */ + +int init_backup(Options *op, Package *p) +{ + FILE *log; + + /* remove the directory, if it already exists */ + + if (directory_exists(op, BACKUP_DIRECTORY)) { + if (!remove_directory(op, BACKUP_DIRECTORY)) { + return FALSE; + } + } + + /* create the backup directory, with perms only for owner */ + + if (!mkdir_recursive(op, BACKUP_DIRECTORY, BACKUP_DIRECTORY_PERMS)) { + return FALSE; + } + + /* create the log file */ + + log = fopen(BACKUP_LOG, "a"); + if (!log) { + ui_error(op, "Unable to create backup log file '%s' (%s).", + BACKUP_LOG, strerror(errno)); + return FALSE; + } + + /* write the version and description */ + + fprintf(log, "%s\n", p->version_string); + fprintf(log, "%s\n", p->description); + + /* close the log file */ + + if (fclose(log) != 0) { + ui_error(op, "Error while closing backup log file '%s' (%s).", + BACKUP_LOG, strerror(errno)); + return FALSE; + } + + /* set the log file permissions */ + + if (chmod(BACKUP_LOG, BACKUP_LOG_PERMS) == -1) { + ui_error(op, "Unable to set file permissions %04o for %s (%s).", + BACKUP_LOG_PERMS, BACKUP_LOG, strerror(errno)); + return FALSE; + } + + return TRUE; + +} /* init_backup() */ + + + +/* + * do_backup() - backup the specified file. If it is a regular file, + * just move it into the backup directory, and add an entry to the log + * file. + */ + +int do_backup(Options *op, const char *filename) +{ + int len, ret, ret_val; + struct stat stat_buf; + char *tmp; + FILE *log; + uint32 crc; + + static int backup_file_number = BACKED_UP_FILE_NUM; + + ret_val = FALSE; + + log = fopen(BACKUP_LOG, "a"); + if (!log) { + ui_error(op, "Unable to open backup log file '%s' (%s).", + BACKUP_LOG, strerror(errno)); + return FALSE; + } + + if (lstat(filename, &stat_buf) == -1) { + ui_error(op, "Unable to determine properties for file '%s' (%s).", + filename, strerror(errno)); + goto done; + } + + if (S_ISREG(stat_buf.st_mode)) { + crc = compute_crc(op, filename); + len = strlen(BACKUP_DIRECTORY) + 64; + tmp = nvalloc(len + 1); + snprintf(tmp, len, "%s/%d", BACKUP_DIRECTORY, backup_file_number); + if (!nvrename(op, filename, tmp)) { + ui_error(op, "Unable to backup file '%s'.", filename); + goto done; + } + + fprintf(log, "%d: %s\n", backup_file_number, filename); + + /* write the filesize, permissions, uid, gid */ + fprintf(log, "%u %04o %d %d\n", crc, stat_buf.st_mode, + stat_buf.st_uid, stat_buf.st_gid); + + free(tmp); + backup_file_number++; + } else if (S_ISLNK(stat_buf.st_mode)) { + tmp = get_symlink_target(op, filename); + + ret = unlink(filename); + if (ret == -1) { + ui_error(op, "Unable to remove symbolic link '%s' (%s).", + filename, strerror(errno)); + goto done; + } + + fprintf(log, "%d: %s\n", BACKED_UP_SYMLINK, filename); + fprintf(log, "%s\n", tmp); + fprintf(log, "%04o %d %d\n", stat_buf.st_mode, + stat_buf.st_uid, stat_buf.st_gid); + free(tmp); + } else if (S_ISDIR(stat_buf.st_mode)) { + + /* XXX IMPLEMENT ME: recursive moving of a directory */ + + ui_error(op, "Unable to backup directory '%s'.", filename); + goto done; + } else { + ui_error(op, "Unable to backup file '%s' (don't know how to deal with " + "file type).", filename); + goto done; + } + + ret_val = TRUE; + + done: + + /* close the log file */ + + if (fclose(log) != 0) { + ui_error(op, "Error while closing backup log file '%s' (%s).", + BACKUP_LOG, strerror(errno)); + ret_val = FALSE; + } + + return ret_val; + +} /* do_backup() */ + + + +int log_install_file(Options *op, const char *filename) +{ + FILE *log; + uint32 crc; + + /* open the log file */ + + log = fopen(BACKUP_LOG, "a"); + if (!log) { + ui_error(op, "Unable to open backup log file '%s' (%s).", + BACKUP_LOG, strerror(errno)); + return FALSE; + } + + fprintf(log, "%d: %s\n", INSTALLED_FILE, filename); + + crc = compute_crc(op, filename); + + fprintf(log, "%u\n", crc); + + /* close the log file */ + + if (fclose(log) != 0) { + ui_error(op, "Error while closing backup log file '%s' (%s).", + BACKUP_LOG, strerror(errno)); + return FALSE; + } + + return TRUE; + +} /* log_install_file() */ + + + +int log_create_symlink(Options *op, const char *filename, const char *target) +{ + FILE *log; + + /* open the log file */ + + log = fopen(BACKUP_LOG, "a"); + if (!log) { + ui_error(op, "Unable to open backup log file '%s' (%s).", + BACKUP_LOG, strerror(errno)); + return FALSE; + } + + fprintf(log, "%d: %s\n", INSTALLED_SYMLINK, filename); + fprintf(log, "%s\n", target); + + /* close the log file */ + + if (fclose(log) != 0) { + ui_error(op, "Error while closing backup log file '%s' (%s).", + BACKUP_LOG, strerror(errno)); + return FALSE; + } + + return TRUE; + +} /* log_create_symlink() */ + + + +static int parse_first_line(const char *buf, int *num, char **filename) +{ + char *c, *local_buf; + + if (!buf || !num || !filename) return FALSE; + + local_buf = nvstrdup(buf); + + c = local_buf; + while ((*c != '\0') && (*c != ':')) { + if (!isdigit(*c)) return FALSE; + c++; + } + if (*c == '\0') return FALSE; + + *c = '\0'; + + *num = strtol(local_buf, NULL, 10); + + c++; + while(isspace(*c)) c++; + + *filename = nvstrdup(c); + + free(local_buf); + + return TRUE; +} + + +static int parse_mode_uid_gid(const char *buf, mode_t *mode, + uid_t *uid, gid_t *gid) +{ + char *c, *local_buf, *str; + + if (!buf || !mode || !uid || !gid) return FALSE; + + local_buf = nvstrdup(buf); + + c = str = local_buf; + while ((*c != '\0') && (isdigit(*c))) c++; + if (*c == '\0') return FALSE; + *c = '\0'; + *mode = strtol(str, NULL, 8); + + str = ++c; + while ((*c != '\0') && (isdigit(*c))) c++; + if (*c == '\0') return FALSE; + *c = '\0'; + *uid = strtol(str, NULL, 10); + + str = ++c; + while ((*c != '\0') && (isdigit(*c))) c++; + *c = '\0'; + *gid = strtol(str, NULL, 10); + + free(local_buf); + + return TRUE; +} + + +static int parse_crc_mode_uid_gid(const char *buf, uint32 *crc, mode_t *mode, + uid_t *uid, gid_t *gid) +{ + char *c, *local_buf, *str; + + if (!buf || !crc || !mode || !uid || !gid) return FALSE; + + local_buf = nvstrdup(buf); + + c = str = local_buf; + while ((*c != '\0') && (isdigit(*c))) c++; + if (*c == '\0') return FALSE; + *c = '\0'; + *crc = strtoul(str, NULL, 10); + + str = ++c; + while ((*c != '\0') && (isdigit(*c))) c++; + if (*c == '\0') return FALSE; + *c = '\0'; + *mode = strtol(str, NULL, 8); + + str = ++c; + while ((*c != '\0') && (isdigit(*c))) c++; + if (*c == '\0') return FALSE; + *c = '\0'; + *uid = strtol(str, NULL, 10); + + str = ++c; + while ((*c != '\0') && (isdigit(*c))) c++; + *c = '\0'; + *gid = strtol(str, NULL, 10); + + free(local_buf); + + return TRUE; +} + + +static int parse_crc(const char *buf, uint32 *crc) +{ + char *c, *local_buf, *str; + + if (!buf || !crc) return FALSE; + + local_buf = nvstrdup(buf); + + c = str = local_buf; + while ((*c != '\0') && (isdigit(*c))) c++; + *c = '\0'; + *crc = strtoul(str, NULL, 10); + + free(local_buf); + + return TRUE; + +} /* parse_crc() */ + + +/* + * do_uninstall() - this function uninstalls a previously installed + * driver, by parsing the BACKUP_LOG file. + */ + +static int do_uninstall(Options *op) +{ + BackupLogEntry *e; + BackupInfo *b; + int i, len, ok; + char *tmpstr; + float percent; + + static const char existing_installation_is_borked[] = + "Your driver installation has been " + "altered since it was initially installed; this may happen, " + "for example, if you have since installed the NVIDIA driver through " + "a mechanism other than the nvidia-installer (such as rpm or " + "with the NVIDIA tarballs). The nvidia-installer will " + "attempt to uninstall as best it can."; + + /* do we even have a backup directory? */ + + if (access(BACKUP_DIRECTORY, F_OK) == -1) { + ui_message(op, "No driver backed up."); + return FALSE; + } + + if ((b = read_backup_log_file(op)) == NULL) return FALSE; + + ok = check_backup_log_entries(op, b); + + if (!ok) { + if (op->logging) { + ui_warn(op, "%s Please see the file '%s' for details.", + existing_installation_is_borked, op->log_file_name); + + } else { + ui_warn(op, existing_installation_is_borked); + } + } + + tmpstr = nvstrcat("Uninstalling ", b->description, " (", + b->version, "):", NULL); + + ui_status_begin(op, tmpstr, "Uninstalling"); + + free(tmpstr); + + /* + * given the list of Backup logfile entries, perform the necessary + * operations: + * + * Step 1: remove everything that was previously installed + * + * Step 2: restore everything that was previously backed up + */ + + for (i = 0; i < b->n; i++) { + + percent = (float) i / (float) (b->n * 2); + + e = &b->e[i]; + + if (!e->ok) continue; + switch (e->num) { + + /* + * This is a file that was installed -- now delete it. + */ + + case INSTALLED_FILE: + if (unlink(e->filename) == -1) { + ui_warn(op, "Unable to remove installed file '%s' (%s).", + e->filename, strerror(errno)); + } + ui_status_update(op, percent, e->filename); + break; + + case INSTALLED_SYMLINK: + if (unlink(e->filename) == -1) { + ui_warn(op, "Unable to remove installed symlink '%s' (%s).", + e->filename, strerror(errno)); + } + ui_status_update(op, percent, e->filename); + break; + } + } + + for (i = 0; i < b->n; i++) { + + percent = (float) (i + b->n) / (float) (b->n * 2); + + e = &b->e[i]; + + if (!e->ok) continue; + + switch (e->num) { + + case INSTALLED_FILE: + case INSTALLED_SYMLINK: + /* nothing to do */ + break; + + case BACKED_UP_SYMLINK: + if (symlink(e->target, e->filename) == -1) { + + /* + * XXX only print this warning if + * check_backup_log_entries() didn't see any problems. + */ + + if (ok) { + ui_warn(op, "Unable to restore symbolic link " + "%s -> %s (%s).", e->filename, e->target, + strerror(errno)); + } else { + ui_log(op, "Unable to restore symbolic link " + "%s -> %s (%s).", e->filename, e->target, + strerror(errno)); + } + } else { + + /* XXX do we need to chmod the symlink? */ + + if (lchown(e->filename, e->uid, e->gid)) { + ui_warn(op, "Unable to restore owner (%d) and group " + "(%d) for symbolic link '%s' (%s).", + e->uid, e->gid, e->filename, strerror(errno)); + } + } + ui_status_update(op, percent, e->filename); + break; + + default: + len = strlen(BACKUP_DIRECTORY) + 64; + tmpstr = nvalloc(len + 1); + snprintf(tmpstr, len, "%s/%d", BACKUP_DIRECTORY, e->num); + if (!nvrename(op, tmpstr, e->filename)) { + ui_warn(op, "Unable to restore file '%s'.", e->filename); + } else { + if (chmod(e->filename, e->mode) == -1) { + ui_warn(op, "Unable to restore permissions %04o for " + "file '%s'.", e->mode, e->filename); + } else { + if (chown(e->filename, e->uid, e->gid)) { + ui_warn(op, "Unable to restore owner (%d) and group " + "(%d) for file '%s' (%s).", + e->uid, e->gid, e->filename, strerror(errno)); + } + } + } + ui_status_update(op, percent, e->filename); + free(tmpstr); + break; + } + } + + ui_status_end(op, "done."); + + /* remove the backup directory */ + + if (!remove_directory(op, BACKUP_DIRECTORY)) { + /* XXX what to do if this fails?... nothing */ + } + + return TRUE; + +} /* do_uninstall() */ + + + + + +/* + * read_backup_log_file() - + */ + +static BackupInfo *read_backup_log_file(Options *op) +{ + struct stat stat_buf; + char *buf, *c, *line, *filename; + int fd, num, line_num = 0; + float percent; + + BackupLogEntry *e; + BackupInfo *b = NULL; + + /* check the permissions of the backup directory */ + + if (stat(BACKUP_DIRECTORY, &stat_buf) == -1) { + ui_error(op, "Unable to get properties of %s (%s).", + BACKUP_DIRECTORY, strerror(errno)); + return NULL; + } + + if ((stat_buf.st_mode & PERM_MASK) != BACKUP_DIRECTORY_PERMS) { + ui_error(op, "The directory permissions of %s have been changed since" + "the directory was created!", BACKUP_DIRECTORY); + return NULL; + } + + if ((fd = open(BACKUP_LOG, O_RDONLY)) == -1) { + ui_error(op, "Failure opening %s (%s).", BACKUP_LOG, strerror(errno)); + return NULL; + } + + if (fstat(fd, &stat_buf) == -1) { + ui_error(op, "Failure getting file properties for %s (%s).", + BACKUP_LOG, strerror(errno)); + return NULL; + } + + if ((stat_buf.st_mode & PERM_MASK) != BACKUP_LOG_PERMS) { + ui_error(op, "The file permissions of %s have been changed since " + "the file was written!", BACKUP_LOG); + return NULL; + } + + /* map the file */ + + buf = mmap(0, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0); + if (!buf) { + ui_error(op, "Unable to mmap file '%s' (%s).", strerror(errno)); + return NULL; + } + + ui_status_begin(op, "Parsing log file:", "Parsing"); + + b = nvalloc(sizeof(BackupInfo)); + + b->version = get_next_line(buf, &c); + if (!b->version || !c) goto parse_error; + + percent = (float) (c - buf) / (float) stat_buf.st_size; + ui_status_update(op, percent, NULL); + + b->description = get_next_line(c, &c); + if (!b->description || !c) goto parse_error; + + b->n = 0; + b->e = NULL; + line_num = 3; + + while(1) { + + percent = (float) (c - buf) / (float) stat_buf.st_size; + ui_status_update(op, percent, NULL); + + /* read and parse the next line */ + + line = get_next_line(c, &c); + if (!line) break; + + if (!parse_first_line(line, &num, &filename)) goto parse_error; + line_num++; + free(line); + + /* grow the BackupLogEntry array */ + + b->n++; + b->e = (BackupLogEntry *) + nvrealloc(b->e, sizeof(BackupLogEntry) * b->n); + + e = &b->e[b->n - 1]; + e->num = num; + e->filename = filename; + e->ok = TRUE; + + switch(e->num) { + + case INSTALLED_FILE: + if ((line = get_next_line(c, &c)) == NULL) goto parse_error; + line_num++; + + if (!parse_crc(line, &e->crc)) goto parse_error; + free(line); + + break; + + case INSTALLED_SYMLINK: + if ((line = get_next_line(c, &c)) == NULL) goto parse_error; + line_num++; + + e->target = line; + + break; + + case BACKED_UP_SYMLINK: + if ((line = get_next_line(c, &c)) == NULL) goto parse_error; + line_num++; + + e->target = line; + + if ((line = get_next_line(c, &c)) == NULL) goto parse_error; + line_num++; + + if (!parse_mode_uid_gid(line, &e->mode, &e->uid, &e->gid)) + goto parse_error; + free(line); + + break; + + default: + if (num < BACKED_UP_FILE_NUM) goto parse_error; + + if ((line = get_next_line(c, &c)) == NULL) goto parse_error; + line_num++; + + if (!parse_crc_mode_uid_gid(line, &e->crc, &e->mode, + &e->uid, &e->gid)) goto parse_error; + free(line); + + break; + } + + if (!c) break; + } + + ui_status_end(op, "done."); + + munmap(buf, stat_buf.st_size); + close(fd); + + return b; + + parse_error: + + ui_status_end(op, "error."); + + munmap(buf, stat_buf.st_size); + close(fd); + + ui_error(op, "Error while parsing line %d of '%s'.", line_num, BACKUP_LOG); + + if (b) free(b); + return NULL; + +} /* read_backup_log_file() */ + + +/* + * check_backup_log_entries() - for each backup log entry, perform + * some basic sanity checks. Set the 'ok' field to FALSE if a + * particular entry should not be uninstalled/restored. + */ + +static int check_backup_log_entries(Options *op, BackupInfo *b) +{ + BackupLogEntry *e; + uint32 crc; + char *tmpstr; + int i, j, len, ret = TRUE; + float percent; + + ui_status_begin(op, "Validating previous installation:", "Validating"); + + for (i = 0; i < b->n; i++) { + + percent = (float) i / (float) (b->n); + + e = &b->e[i]; + + switch (e->num) { + + case INSTALLED_FILE: + + /* check if the file is still there, and has the same crc */ + + if (access(e->filename, F_OK) == -1) { + ui_log(op, "Unable to access previously installed file " + "'%s' (%s).", e->filename, strerror(errno)); + ret = e->ok = FALSE; + } else { + crc = compute_crc(op, e->filename); + + if (crc != e->crc) { + ui_log(op, "The previously installed file '%s' has a " + "different checksum (%lu) than " + "when it was installed (%lu). %s will not be " + "uninstalled.", + e->filename, crc, e->crc, e->filename); + ret = e->ok = FALSE; + } + } + ui_status_update(op, percent, e->filename); + + break; + + case INSTALLED_SYMLINK: + + /* + * check if the symlink is still there, and has the same + * target + */ + + if (access(e->filename, F_OK) == -1) { + ui_log(op, "Unable to access previously installed " + "symlink '%s' (%s).", e->filename, strerror(errno)); + ret = e->ok = FALSE; + } else { + tmpstr = get_symlink_target(op, e->filename); + if (!tmpstr) { + ret = e->ok = FALSE; + } else { + if (strcmp(tmpstr, e->target) != 0) { + ui_log(op, "The previously installed symlink '%s' " + "has target '%s', but it was installed " + "with target '%s'. %s will not be " + "uninstalled.", + e->filename, tmpstr, e->target, e->filename); + ret = e->ok = FALSE; + + /* + * if an installed symbolic link has a + * different target, then we don't remove it. + * This also means that we shouldn't attempt + * to restore a backed up symbolic link of the + * same name, or whose target matches this + * target. + */ + + for (j = 0; j < b->n; j++) { + if ((b->e[j].num == BACKED_UP_SYMLINK) && + (strcmp(b->e[j].filename, e->filename) == 0)) { + b->e[j].ok = FALSE; + } + } + } + free(tmpstr); + } + } + ui_status_update(op, percent, e->filename); + + break; + + case BACKED_UP_SYMLINK: + + /* nothing to do */ + + break; + + default: + + /* + * this is a backed up file; check that the file is still + * present and has the same crc + */ + + len = strlen(BACKUP_DIRECTORY) + 64; + tmpstr = nvalloc(len + 1); + snprintf(tmpstr, len, "%s/%d", BACKUP_DIRECTORY, e->num); + if (access(tmpstr, F_OK) == -1) { + ui_log(op, "Unable to access backed up file '%s' " + "(saved as '%s') (%s).", + e->filename, tmpstr, strerror(errno)); + ret = e->ok = FALSE; + } else { + crc = compute_crc(op, tmpstr); + + if (crc != e->crc) { + ui_log(op, "Backed up file '%s' (saved as '%s) has " + "different checksum (%lu) than" + "when it was backed up (%lu). %s will not be " + "restored.", e->filename, tmpstr, + crc, e->crc, e->filename); + ret = e->ok = FALSE; + } + } + ui_status_update(op, percent, tmpstr); + free(tmpstr); + break; + } + } + + ui_status_end(op, "done."); + + return (ret); + +} /* check_backup_log_entries() */ + + + +/* + * get_installed_driver_version_and_descr() - determine the currently + * installed driver version and description. Returns the description + * string if a previous driver is installed. Returns NULL if no + * driver is currently installed. + * + * XXX for now, we'll just get the installed driver version by reading + * BACKUP_LOG. This is probably insufficient, though; how do we + * detect a driver that was installed prior to the new installer? + * Should we look for the installed files on the system and pull nvid + * from them? + * + * XXX we should probably check the file permissions of BACKUP_LOG. + */ + +char *get_installed_driver_version_and_descr(Options *op, int *major, + int *minor, int *patch) +{ + struct stat stat_buf; + char *c, *version = NULL, *descr = NULL, *buf = NULL; + int fd = -1; + + if ((fd = open(BACKUP_LOG, O_RDONLY)) == -1) goto done; + + if (fstat(fd, &stat_buf) == -1) goto done; + + /* map the file */ + + buf = mmap(0, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0); + if (!buf) goto done; + + version = get_next_line(buf, &c); + if (!version) goto done; + + if (!nvid_version(version, major, minor, patch)) goto done; + + descr = get_next_line(c, NULL); + + done: + if (version) free(version); + if (buf) munmap(buf, stat_buf.st_size); + if (fd != -1) close(fd); + return descr; + +} /* get_installed_driver_version_and_descr() */ + + + +/* + * check_for_existing_driver() - Get the existing driver description + * and version from BACKUP_LOG. If an existing driver is present, ask + * the user if they really want it to be uninstalled. + * + * Returns TRUE if it is OK to continue with the installation process. + * Returns FALSE if the user decided they didn't want to continue with + * installation. + * + * If we are only installing a kernel module, then there must be an + * existing driver installation, and the version of that installation + * must match the module we're trying to install. + */ + +int check_for_existing_driver(Options *op, Package *p) +{ + int major, minor, patch; + char *descr; + + if (!check_for_existing_rpms(op)) return FALSE; + + descr = get_installed_driver_version_and_descr(op, &major, &minor, &patch); + + if (op->kernel_module_only) { + if (!descr) { + ui_error(op, "No NVIDIA driver is currently installed; the " + "'--kernel-module-only' option can only be used " + "to install the NVIDIA kernel module on top of an " + "existing driver installation."); + return FALSE; + } else { + if ((p->major != major) || + (p->minor != minor) || + (p->patch != patch)) { + ui_error(op, "The '--kernel-module-only' option can only be " + "used to install a kernel module on top of an " + "existing driver installation of the same driver " + "version. The existing driver installation is " + "%d.%d-%d, but the kernel module is %d.%d-%d\n", + major, minor, patch, p->major, p->minor, p->patch); + return FALSE; + } else { + return TRUE; + } + } + } + + if (!descr) return TRUE; + + /* + * XXX we could do a comparison, here, to check that the Package + * version is greater than or equal to the installed version, and + * issue a warning if the user is downgrading. That doesn't seem + * necessary, though; I can't think of a good reason why + * downgrading is any different than upgrading. + */ + + if (!ui_yes_no(op, TRUE, "There appears to already be a driver installed " + "on your system (version: %d.%d-%d). As part of " + "installing this driver (version: %d.%d-%d), the existing " + "driver will be uninstalled. Are you sure you want to " + "continue? ('no' will abort installation)", + major, minor, patch, p->major, p->minor, p->patch)) { + + free(descr); + ui_log(op, "Installation aborted."); + return FALSE; + } + + free(descr); + return TRUE; + +} /* check_for_existing_driver() */ + + + +/* + * uninstall_existing_driver() - check if there is a driver already + * installed, and if there is, uninstall it. + * + * Currently, nothing about this function should cause installation to + * stop (so it always returns TRUE). + */ + +int uninstall_existing_driver(Options *op, const int interactive) +{ + int major, minor, patch, ret; + char *descr; + + descr = get_installed_driver_version_and_descr(op, &major, &minor, &patch); + if (!descr) { + if (interactive) { + ui_message(op, "There is no NVIDIA driver currently installed."); + } + return TRUE; + } + + ret = do_uninstall(op); + + if (ret) { + if (interactive) { + ui_message(op, "Uninstallation of existing driver: %s (%d.%d-%d) " + "is complete.", descr, major, minor, patch); + } else { + ui_log(op, "Uninstallation of existing driver: %s (%d.%d-%d) " + "is complete.", descr, major, minor, patch); + } + } else { + ui_error(op, "Uninstallation failed."); + } + + free(descr); + + return TRUE; + +} /* uninstall_existing_driver() */ + + + +/* + * report_driver_information() - report basic information about the + * currently installed driver. + */ + +int report_driver_information(Options *op) +{ + int major, minor, patch; + char *descr; + + descr = get_installed_driver_version_and_descr(op, &major, &minor, &patch); + if (!descr) { + ui_message(op, "There is no NVIDIA driver currently installed."); + return FALSE; + } + + ui_message(op, "The currently installed driver is: '%s' " + "(version: %d.%d-%d).", descr, major, minor, patch); + + free(descr); + return TRUE; + +} /* report_driver_information() */ + + + +/* + * test_installed_files() - + */ + +int test_installed_files(Options *op) +{ + BackupInfo *b; + int ret; + + b = read_backup_log_file(op); + if (!b) return FALSE; + + ret = sanity_check_backup_log_entries(op, b); + + /* XXX should free resources associated with b */ + + return ret; + +} /* test_installed_files() */ + + + +/* + * find_installed_file() - scan the backup log file for the specified + * filename; return TRUE if the filename is listed as an installed file. + */ + +int find_installed_file(Options *op, char *filename) +{ + BackupInfo *b; + BackupLogEntry *e; + int i; + + if ((b = read_backup_log_file(op)) == NULL) return FALSE; + + for (i = 0; i < b->n; i++) { + e = &b->e[i]; + if ((e->num == INSTALLED_FILE) && strcmp(filename, e->filename) == 0) { + + /* XXX should maybe compare inodes rather than + filenames? */ + + return TRUE; + } + } + + /* XXX need to free b */ + + return FALSE; + +} /* find_installed_file() */ + + + +/* + * sanity_check_backup_log_entries() - this function is very similar + * to check_backup_log_entries(); however, it varies in it's error + * messages because it is used as part of the sanity check path. + */ + +static int sanity_check_backup_log_entries(Options *op, BackupInfo *b) +{ + BackupLogEntry *e; + uint32 crc; + char *tmpstr; + int i, len, ret = TRUE; + float percent; + + ui_status_begin(op, "Validating installation:", "Validating"); + + for (i = 0; i < b->n; i++) { + + e = &b->e[i]; + + switch (e->num) { + + case INSTALLED_FILE: + + /* check if the file is still there, and has the same crc */ + + if (access(e->filename, F_OK) == -1) { + ui_error(op, "The installed file '%s' no longer exists.", + e->filename); + ret = FALSE; + } else { + crc = compute_crc(op, e->filename); + + if (crc != e->crc) { + ui_error(op, "The installed file '%s' has a different " + "checksum (%lu) than when it was installed " + "(%lu).", e->filename, crc, e->crc); + ret = FALSE; + } + } + break; + + case INSTALLED_SYMLINK: + + /* + * check if the symlink is still there, and has the same + * target + */ + + if (access(e->filename, F_OK) == -1) { + ui_error(op, "The installed symbolic link '%s' no " + "longer exists.", e->filename); + ret = FALSE; + } else { + tmpstr = get_symlink_target(op, e->filename); + if (!tmpstr) { + ret = FALSE; + } else { + if (strcmp(tmpstr, e->target) != 0) { + ui_error(op, "The installed symbolic link '%s' " + "has target '%s', but it was installed " + "with target '%s'.", + e->filename, tmpstr, e->target); + ret = FALSE; + } + free(tmpstr); + } + } + break; + + case BACKED_UP_SYMLINK: + + /* nothing to do */ + + break; + + default: + + /* + * this is a backed up file; check that the file is still + * present and has the same crc + */ + + len = strlen(BACKUP_DIRECTORY) + 64; + tmpstr = nvalloc(len + 1); + snprintf(tmpstr, len, "%s/%d", BACKUP_DIRECTORY, e->num); + if (access(tmpstr, F_OK) == -1) { + ui_error(op, "The backed up file '%s' (saved as '%s') " + "no longer exists.", e->filename, tmpstr); + ret = FALSE; + } else { + crc = compute_crc(op, tmpstr); + + if (crc != e->crc) { + ui_error(op, "Backed up file '%s' (saved as '%s) has a " + "different checksum (%lu) than" + "when it was backed up (%lu).", + e->filename, tmpstr, crc, e->crc); + ret = FALSE; + } + } + free(tmpstr); + break; + } + + percent = (float) i / (float) (b->n); + ui_status_update(op, percent, e->filename); + } + + ui_status_end(op, "done."); + + return ret; + +} /* sanity_check_backup_log_entries */ diff --git a/backup.h b/backup.h new file mode 100644 index 0000000..c571296 --- /dev/null +++ b/backup.h @@ -0,0 +1,48 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + */ + +#ifndef __NVIDIA_INSTALLER_BACKUP_H__ +#define __NVIDIA_INSTALLER_BACKUP_H__ + +#include "nvidia-installer.h" + +#define INSTALLED_SYMLINK 0 +#define INSTALLED_FILE 1 +#define BACKED_UP_SYMLINK 2 +#define BACKED_UP_FILE_NUM 100 + +int init_backup (Options*, Package*); +int do_backup (Options*, const char*); +int log_install_file (Options*, const char*); +int log_create_symlink (Options*, const char*, const char*); +int get_installed_driver_version(Options*, int*, int*, int*); +int check_for_existing_driver (Options*, Package*); +int uninstall_existing_driver (Options*, const int); +int report_driver_information (Options*); + +char *get_installed_driver_version_and_descr(Options *, int *, int *, int *); +int test_installed_files(Options *op); +int find_installed_file(Options *op, char *filename); + +#endif /* __NVIDIA_INSTALLER_BACKUP_H__ */ diff --git a/command-list.c b/command-list.c new file mode 100644 index 0000000..ae63036 --- /dev/null +++ b/command-list.c @@ -0,0 +1,714 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * command_list.c - this source file contains functions for building + * and executing a commandlist (the list of operations to perform to + * actually do an install). + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> + +#include "nvidia-installer.h" +#include "command-list.h" +#include "user-interface.h" +#include "backup.h" +#include "misc.h" +#include "files.h" +#include "kernel.h" + + + +static void find_existing_files(Package *p, FileList *l, unsigned int); +static void find_conflicting_kernel_modules(Options *op, + Package *p, FileList *l); + +static void add_command (CommandList *c, int cmd, ...); + +static void find_matches (const char*, const char*, FileList*, const int); +static void add_file_to_list(const char*, const char*, FileList*); + +static void append_to_rpm_file_list(Options *op, Command *c); + + +/* + * build_command_list() - construct a list of all the things to do + * during the installation. This consists of: + * + * - backing and removing up conflicting files + * - installing all the installable files + * - create any needed symbolic links + * - executing any nessary commands + * - running `ldconfig` and `depmod -aq` + * + * If we are only installing the kernel module, then we trim back all + * the stuff that we don't need to do. + */ + +CommandList *build_command_list(Options *op, Package *p) +{ + FileList *l; + CommandList *c; + int i, cmd; + unsigned int installable_files; + char *tmp; + + installable_files = get_installable_file_mask(op); + + l = (FileList *) nvalloc(sizeof(FileList)); + c = (CommandList *) nvalloc(sizeof(CommandList)); + + /* find any possible conflicting libraries */ + + if (!op->kernel_module_only) { + + find_conflicting_xfree86_libraries + (DEFAULT_XFREE86_INSTALLATION_PREFIX,l); + + if (strcmp(DEFAULT_XFREE86_INSTALLATION_PREFIX, + op->xfree86_prefix) != 0) + find_conflicting_xfree86_libraries(op->xfree86_prefix, l); + + find_conflicting_opengl_libraries + (DEFAULT_OPENGL_INSTALLATION_PREFIX,l); + + if (strcmp(DEFAULT_OPENGL_INSTALLATION_PREFIX, op->opengl_prefix) != 0) + find_conflicting_opengl_libraries(op->opengl_prefix, l); + } + + find_conflicting_kernel_modules(op, p, l); + + /* + * find any existing files that clash with what we're going to + * install + */ + + find_existing_files(p, l, installable_files | FILE_TYPE_SYMLINK); + + /* condense the file list */ + + condense_file_list(l); + + /* check the conflicting file list for any installed files */ + + if (op->kernel_module_only) { + for (i = 0; i < l->num; i++) { + if (find_installed_file(op, l->filename[i])) { + ui_error(op, "The file '%s' already exists as part of this " + "driver installation.", l->filename[i]); + return NULL; + } + } + } + + /* + * all of the files in the conflicting file list should be backed + * up or deleted + */ + + cmd = op->no_backup ? DELETE_CMD : BACKUP_CMD; + + for (i = 0; i < l->num; i++) + add_command(c, cmd, l->filename[i], NULL, 0); + + /* Add all the installable files to the list */ + + for (i = 0; i < p->num_entries; i++) { + if (p->entries[i].flags & installable_files) { + add_command(c, INSTALL_CMD, + p->entries[i].file, + p->entries[i].dst, + p->entries[i].mode); + } + } + + + /* create any needed symbolic links */ + + for (i = 0; i < p->num_entries; i++) { + if (p->entries[i].flags & FILE_TYPE_SYMLINK) { + add_command(c, SYMLINK_CMD, p->entries[i].dst, + p->entries[i].target); + } + } + + /* find any commands we should run */ + + for (i = 0; i < p->num_entries; i++) { + if (p->entries[i].flags & FILE_TYPE_KERNEL_MODULE_CMD) { + add_command(c, RUN_CMD, p->entries[i].file, NULL, 0); + } + } + + /* + * if "--no-abi-note" was requested, scan for any OpenGL + * libraries, and run the following command on them: + * + * objcopy --remove-section=.note.ABI-tag <filename> 2> /dev/null ; true + */ + + if (op->no_abi_note) { + for (i = 0; i < p->num_entries; i++) { + if (p->entries[i].flags & FILE_TYPE_OPENGL_LIB) { + tmp = nvstrcat(op->utils[OBJCOPY], + " --remove-section=.note.ABI-tag ", + p->entries[i].dst, + " 2> /dev/null ; true", NULL); + add_command(c, RUN_CMD, tmp); + nvfree(tmp); + } + } + } + + /* finally, run ldconfig and depmod */ + + add_command(c, RUN_CMD, op->utils[LDCONFIG]); + + /* + * If we are building for a non-running kernel, specify that + * kernel name on the depmod commandline line (see depmod(8) + * manpage for details). Patch provided by Nigel Spowage + * <Nigel.Spowage@energis.com> + */ + + if ( op->kernel_name && op->kernel_name[0] ) { + tmp = nvstrcat(op->utils[DEPMOD], " -aq ", op->kernel_name, NULL); + } else { + tmp = nvstrcat(op->utils[DEPMOD], " -aq", NULL); + } + add_command(c, RUN_CMD, tmp); + nvfree(tmp); + + /* + * if on SuSE or United Linux, also do `/usr/bin/chrc.config + * SCRIPT_3D no` + */ + + if (((op->distro == SUSE) || (op->distro == UNITED_LINUX)) && + (access("/usr/bin/chrc.config", X_OK) == 0)) { + add_command(c, RUN_CMD, "/usr/bin/chrc.config SCRIPT_3D no"); + } + + /* free the FileList */ + + for (i = 0; i < l->num; i++) free(l->filename[i]); + free(l->filename); + free(l); + + return c; + +} /* build_command_list() */ + + + +/* + * free_command_list() - free the specified commandlist + */ + +void free_command_list(Options *op, CommandList *cl) +{ + int i; + Command *c; + + if (!cl) return; + + for (i = 0; i < cl->num; i++) { + c = &cl->cmds[i]; + if (c->s0) free(c->s0); + if (c->s1) free(c->s1); + } + + if (cl->cmds) free(cl->cmds); + + free(cl); + +} /* free_command_list() */ + + + +/* + * execute_command_list() - execute the commands in the command list. + * + * If any failure occurs, ask the user if they would like to continue. + */ + +int execute_command_list(Options *op, CommandList *c, + const char *title, const char *msg) +{ + int i, ret; + char *data; + float percent; + + ui_status_begin(op, title, msg); + + for (i = 0; i < c->num; i++) { + + percent = (float) i / (float) c->num; + + switch (c->cmds[i].cmd) { + + case INSTALL_CMD: + ui_expert(op, "Installing: %s --> %s", + c->cmds[i].s0, c->cmds[i].s1); + ui_status_update(op, percent, "Installing: %s", c->cmds[i].s1); + + ret = install_file(op, c->cmds[i].s0, c->cmds[i].s1, + c->cmds[i].mode); + if (!ret) { + ret = continue_after_error(op, "Cannot install %s", + c->cmds[i].s1); + if (!ret) return FALSE; + } else { + log_install_file(op, c->cmds[i].s1); + append_to_rpm_file_list(op, &c->cmds[i]); + } + break; + + case RUN_CMD: + ui_expert(op, "Executing: %s", c->cmds[i].s0); + ui_status_update(op, percent, "Executing: `%s` " + "(this may take a moment...)", c->cmds[i].s0); + ret = run_command(op, c->cmds[i].s0, &data, TRUE, 0, TRUE); + if (ret != 0) { + ui_error(op, "Failed to execute `%s`: %s", + c->cmds[i].s0, data); + ret = continue_after_error(op, "Failed to execute `%s`", + c->cmds[i].s0); + if (!ret) return FALSE; + } + if (data) free(data); + break; + + case SYMLINK_CMD: + ui_expert(op, "Creating symlink: %s -> %s", + c->cmds[i].s0, c->cmds[i].s1); + ui_status_update(op, percent, "Creating symlink: %s", + c->cmds[i].s1); + + ret = symlink(c->cmds[i].s1, c->cmds[i].s0); + if (ret == -1) { + ret = continue_after_error(op, "Cannot create symlink %s (%s)", + c->cmds[i].s0, strerror(errno)); + if (!ret) return FALSE; + } else { + log_create_symlink(op, c->cmds[i].s0, c->cmds[i].s1); + } + break; + + case BACKUP_CMD: + ui_expert(op, "Backing up: %s", c->cmds[i].s0); + ui_status_update(op, percent, "Backing up: %s", c->cmds[i].s0); + + ret = do_backup(op, c->cmds[i].s0); + if (!ret) { + ret = continue_after_error(op, "Cannot backup %s", + c->cmds[i].s0); + if (!ret) return FALSE; + } + break; + + case DELETE_CMD: + ui_expert(op, "Deleting: %s", c->cmds[i].s0); + ret = unlink(c->cmds[i].s0); + if (ret == -1) { + ret = continue_after_error(op, "Cannot delete %s", + c->cmds[i].s0); + if (!ret) return FALSE; + } + break; + + default: + /* XXX should never get here */ + return FALSE; + break; + } + } + + ui_status_end(op, "done."); + + return TRUE; + +} /* execute_command_list() */ + + +/* + *************************************************************************** + * local static routines + *************************************************************************** + */ + +static const char *__libdirs[] = { "lib", "lib64", NULL }; + +/* + * find_conflicting_xfree86_libraries() - search for conflicting + * libraries under the XFree86 installation prefix, for all possible + * libdirs. + */ + +void find_conflicting_xfree86_libraries(const char *xprefix, FileList *l) +{ + char *s; + const char *libdir; + int i; + + for (i = 0; __libdirs[i]; i++) { + + libdir = __libdirs[i]; + + /* + * [xprefix]/[libdir]/libGL.* + * [xprefix]/[libdir]/libGLcore.* + * [xprefix]/[libdir]/libXvMCNVIDIA* + * [xprefix]/[libdir]/libGLwrapper.* + */ + + s = nvstrcat(xprefix, "/", libdir, NULL); + find_matches(s, "libGL.", l, FALSE); + find_matches(s, "libGLcore.", l, FALSE); + find_matches(s, "libXvMCNVIDIA", l, FALSE); + find_matches(s, "libGLwrapper.", l, FALSE); + free(s); + + /* + * [xprefix]/[libdir]/tls/libGL.* + * [xprefix]/[libdir]/tls/libGLcore.* + * [xprefix]/[libdir]/tls/libXvMCNVIDIA* + * [xprefix]/[libdir]/tls/libGLwrapper.* + */ + + s = nvstrcat(xprefix, "/", libdir, "/tls", NULL); + find_matches(s, "libGL.", l, FALSE); + find_matches(s, "libGLcore.", l, FALSE); + find_matches(s, "libXvMCNVIDIA", l, FALSE); + find_matches(s, "libGLwrapper.", l, FALSE); + free(s); + + /* + * [xprefix]/[libdir]/modules/extensions/libGLcore.* + * [xprefix]/[libdir]/modules/extensions/libglx.* + * [xprefix]/[libdir]/modules/extensions/libGLwrapper.* + */ + + s = nvstrcat(xprefix, "/", libdir, "/modules/extensions", NULL); + find_matches(s, "libglx.", l, FALSE); + find_matches(s, "libGLcore.", l, FALSE); + find_matches(s, "libGLwrapper.", l, FALSE); + free(s); + } + +} /* find_conflicting_xfree86_libraries() */ + + + +/* + * find_conflicting_opengl_libraries() - search for conflicting + * libraries under the OpenGL installation prefix, for all possible + * libdirs. + */ + +void find_conflicting_opengl_libraries(const char *glprefix, FileList *l) +{ + char *s; + const char *libdir; + int i; + + for (i = 0; __libdirs[i]; i++) { + + libdir = __libdirs[i]; + + /* + * [glprefix]/[libdir]/libGL.* + * [glprefix]/[libdir]/libGLcore.* + * [glprefix]/[libdir]/libGLwrapper.* + * [glprefix]/[libdir]/tls/libGL.* + * [glprefix]/[libdir]/tls/libGLcore.* + * [glprefix]/[libdir]/tls/libGLwrapper.* + */ + + s = nvstrcat(glprefix, "/", libdir, NULL); + find_matches(s, "libGL.", l, FALSE); + find_matches(s, "libGLcore.", l, FALSE); + find_matches(s, "libGLwrapper.", l, FALSE); + find_matches(s, "libnvidia-tls.", l, FALSE); + free(s); + + s = nvstrcat(glprefix, "/", libdir, "/tls", NULL); + find_matches(s, "libGL.", l, FALSE); + find_matches(s, "libGLcore.", l, FALSE); + find_matches(s, "libGLwrapper.", l, FALSE); + find_matches(s, "libnvidia-tls.", l, FALSE); + free(s); + } + +} /* find_conflicting_opengl_libraries() */ + + + +/* + * find_conflicting_kernel_modules() - search for conflicting kernel + * modules under the kernel module installation prefix. + * + * XXX rather than use a fixed list of prefixes, maybe we should scan for the + * kernel module name anywhere under /lib/modules/`uname -r`/ ? + */ + +static void find_conflicting_kernel_modules(Options *op, + Package *p, FileList *l) +{ + int i, n = 0; + char *prefixes[5]; + char *tmp = get_kernel_name(op); + + prefixes[0] = op->kernel_module_installation_path; + + if (tmp) { + prefixes[1] = nvstrcat("/lib/modules/", tmp, "/kernel/drivers/video", NULL); + prefixes[2] = nvstrcat("/lib/modules/", tmp, "/kernel/drivers/char", NULL); + prefixes[3] = nvstrcat("/lib/modules/", tmp, "/video", NULL); + prefixes[4] = NULL; + } else { + prefixes[1] = NULL; + } + + /* look for all the conflicting module names in all the possible prefixes */ + + for (i = 0; prefixes[i]; i++) { + for (n = 0; p->bad_module_filenames[n]; n++) { + find_matches(prefixes[i], p->bad_module_filenames[n], l, TRUE); + } + } + + /* free any prefixes we nvstrcat()ed above */ + + for (i = 1; prefixes[i]; i++) { + nvfree(prefixes[i]); + } + +} /* find_conflicting_kernel_modules() */ + + + +/* + * find_existing_files() - given a Package description, search for any + * of the package files which already exist, and add them to the + * FileList. + */ + +static void find_existing_files(Package *p, FileList *l, unsigned int flag) +{ + int i; + struct stat stat_buf; + + for (i = 0; i < p->num_entries; i++) { + if (p->entries[i].flags & flag) { + if (lstat(p->entries[i].dst, &stat_buf) == 0) { + add_file_to_list(NULL, p->entries[i].dst, l); + } + } + } +} /* find_existing_files() */ + + + +/* + * condense_file_list() - Take a FileList stucture and delete any + * duplicate entries in the list. This is a pretty brain dead + * brute-force algorithm. + */ + +void condense_file_list(FileList *l) +{ + char **s = NULL; + int n = 0, i, j, match; + + for (i = 0; i < l->num; i++) { + match = FALSE; + for (j = 0; j < n; j++) { + if (strcmp(l->filename[i], s[j]) == 0) { + match = TRUE; + break; + } + } + + if (!match) { + s = (char **) nvrealloc(s, sizeof(char *) * (n + 1)); + s[n] = nvstrdup(l->filename[i]); + n++; + } + } + + for (i = 0; i < l->num; i++) free(l->filename[i]); + free(l->filename); + + l->filename = s; + l->num = n; + +} /* condense_file_list() */ + + + +/* + * add_command() - grow the commandlist and append the new command, + * parsing the variable argument list. + */ + +static void add_command(CommandList *c, int cmd, ...) +{ + int n = c->num; + char *s; + va_list ap; + + c->cmds = (Command *) nvrealloc(c->cmds, sizeof(Command) * (n + 1)); + + c->cmds[n].cmd = cmd; + c->cmds[n].s0 = NULL; + c->cmds[n].s1 = NULL; + c->cmds[n].mode = 0x0; + + va_start(ap, cmd); + + switch (cmd) { + case INSTALL_CMD: + s = va_arg(ap, char *); + c->cmds[n].s0 = nvstrdup(s); + s = va_arg(ap, char *); + c->cmds[n].s1 = nvstrdup(s); + c->cmds[n].mode = va_arg(ap, mode_t); + break; + case BACKUP_CMD: + s = va_arg(ap, char *); + c->cmds[n].s0 = nvstrdup(s); + break; + case RUN_CMD: + s = va_arg(ap, char *); + c->cmds[n].s0 = nvstrdup(s); + break; + case SYMLINK_CMD: + s = va_arg(ap, char *); + c->cmds[n].s0 = nvstrdup(s); + s = va_arg(ap, char *); + c->cmds[n].s1 = nvstrdup(s); + break; + case DELETE_CMD: + s = va_arg(ap, char *); + c->cmds[n].s0 = nvstrdup(s); + break; + default: + break; + } + + va_end(ap); + + c->num++; + +} /* add_command() */ + + + +/* + * find_matches() - given a directory, a filename, and an existing + * FileList data structure, open the specified directory, and look for + * any entries that match the filename. + * + * If the parameter 'exact' is TRUE, then the filenames must match + * exactly. If 'exact' is FALSE, then only the beginning of the + * directory entry name must match the filename in question. + * + * This could alternatively be implemented using glob(3). + */ + +static void find_matches(const char *directory, const char *filename, + FileList *l, const int exact) +{ + struct stat stat_buf; + struct dirent *ent; + int len; + DIR *dir; + + if (lstat(directory, &stat_buf) == -1) return; + if (S_ISDIR(stat_buf.st_mode) == 0) return; + if ((dir = opendir(directory)) == NULL) return; + + len = strlen(filename); + + while ((ent = readdir(dir)) != NULL) { + if (exact) { + if (strcmp(ent->d_name, filename) == 0) { + add_file_to_list(directory, ent->d_name, l); + } + } else { + if (strncmp(ent->d_name, filename, len) == 0) { + add_file_to_list(directory, ent->d_name, l); + } + } + } + + closedir (dir); + +} /* find_matches() */ + + + +/* + * add_file_to_list() - concatenate the given directory and filename, + * appending to the FileList structure. If the 'directory' parameter + * is NULL, then just append the filename to the FileList. + */ + +static void add_file_to_list(const char *directory, + const char *filename, FileList *l) +{ + int len, n = l->num; + + l->filename = (char **) nvrealloc(l->filename, sizeof(char *) * (n + 1)); + + if (directory) { + len = strlen(filename) + strlen(directory) + 2; + l->filename[n] = (char *) nvalloc(len); + snprintf(l->filename[n], len, "%s/%s", directory, filename); + } else { + l->filename[n] = nvstrdup(filename); + } + l->num++; + +} /* add_file_to_list() */ + + +static void append_to_rpm_file_list(Options *op, Command *c) +{ + FILE *file; + + if (!op->rpm_file_list) return; + + file = fopen(op->rpm_file_list, "a"); + fprintf(file, "%%attr (%04o, root, root) %s\n", c->mode, c->s1); + fclose(file); +} diff --git a/command-list.h b/command-list.h new file mode 100644 index 0000000..d25415e --- /dev/null +++ b/command-list.h @@ -0,0 +1,90 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + */ + +#ifndef __NVIDIA_INSTALLER_COMMAND_LIST_H__ +#define __NVIDIA_INSTALLER_COMMAND_LIST_H__ + + +/* + * Command and CommandList structures - data types for describing what + * operations to perform to do an install. The semantics of the s0, + * s1, and mode fields vary, depending upon the value of the cmd field + * (see the constants below). + */ + +typedef struct { + int cmd; + char *s0; + char *s1; + mode_t mode; +} Command; + +typedef struct { + int num; + Command *cmds; +} CommandList; + + +/* + * structure for storing a list of filenames. + */ + +typedef struct { + int num; + char **filename; +} FileList; + + + +/* + * commands: + * + * INSTALL - install the file named is s0, giving it the name in s1; + * assign s1 the permissions specified by mode + * + * BACKUP - move the file named in s0, storing it in the backup + * directory and recording the data as appropriate. + * + * RUN - execute the string in s0 + * + * SYMLINK - create a symbolic link named s0, pointing at the filename + * specified in s1. + */ + +#define INSTALL_CMD 1 +#define BACKUP_CMD 2 +#define RUN_CMD 3 +#define SYMLINK_CMD 4 +#define DELETE_CMD 5 + + +CommandList *build_command_list(Options*, Package *); +void free_command_list(Options*, CommandList*); +int execute_command_list(Options*, CommandList*, const char*, const char*); + +void find_conflicting_xfree86_libraries(const char*, FileList*); +void find_conflicting_opengl_libraries(const char*, FileList*); +void condense_file_list(FileList *l); + +#endif /* __NVIDIA_INSTALLER_COMMAND_LIST_H__ */ @@ -0,0 +1,116 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * crc.c - this source file contains code for generating a 32-bit + * checksum. Based on the 16 bit CRC algorithm in Numerical Recipes + * 2nd Ed., pp 900-901. + * + * For a generator, we use the polynomial: + * + * x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + + * x^5 + x^4 + x^2 + x^1 + 1. + * + * (without bit 32) + * + * See the source for cksum in GNU textutils for additional + * references. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> + +#include "nvidia-installer.h" +#include "user-interface.h" +#include "misc.h" + +#define BIT(x) (1 << (x)) +#define CRC_GEN_MASK (BIT(26) | BIT(23) | BIT(22) | BIT(16) | BIT(12) | \ + BIT(11) | BIT(10) | BIT(8) | BIT(7) | BIT(5) | \ + BIT(4) | BIT(2) | BIT(1) | BIT(0)) + + +static uint32 crc_init(uint32 crc) +{ + int i; + uint32 ans = crc; + + for (i=0; i < 8; i++) { + if (ans & 0x80000000) { + ans = (ans << 1) ^ CRC_GEN_MASK; + } else { + ans <<= 1; + } + } + return ans; + +} /* crc_init() */ + + + +uint32 compute_crc(Options *op, const char *filename) +{ + uint32 cword = ~0; + static uint32 *crctab = NULL; + uint8 *buf; + int i, fd; + struct stat stat_buf; + size_t len; + + if (!crctab) { + crctab = (uint32 *) nvalloc(sizeof(uint32) * 256); + for (i=0; i < 256; i++) { + crctab[i] = crc_init(i << 24); + } + } + + if ((fd = open(filename, O_RDONLY)) == -1) goto fail; + if (fstat(fd, &stat_buf) == -1) goto fail; + + len = stat_buf.st_size; + + buf = mmap(0, len, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0); + if (buf == (void *) -1) goto fail; + + for (i = 0; i < len; i++) { + cword = crctab[buf[i] ^ (cword >> 24)] ^ (cword << 8); + } + + if (munmap(buf, len) == -1) goto fail; + if (close(fd) == -1) goto fail; + + return cword; + + fail: + + ui_warn(op, "Unable to compute CRC for file '%s' (%s).", + filename, strerror(errno)); + + return cword; + +} /* compute_crc() */ @@ -0,0 +1,30 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + */ + +#ifndef __NVIDIA_INSTALLER_CRC_H__ +#define __NVIDIA_INSTALLER_CRC_H__ + +uint32 compute_crc(Options *op, const char *filename); + +#endif /* __NVIDIA_INSTALLER_CRC_H__ */ @@ -0,0 +1,1481 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * files.c - this source file contains routines for manipulating + * files and directories for the nv-instaler. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/mman.h> +#include <sys/utsname.h> +#include <stdlib.h> +#include <limits.h> +#include <libgen.h> +#include <utime.h> +#include <time.h> + +#include "nvidia-installer.h" +#include "user-interface.h" +#include "files.h" +#include "misc.h" +#include "precompiled.h" + +/* + * remove_directory() - recursively delete a direcotry (`rm -rf`) + */ + +int remove_directory(Options *op, const char *victim) +{ + struct stat stat_buf; + DIR *dir; + struct dirent *ent; + char *filename; + int len; + + if (lstat(victim, &stat_buf) == -1) { + ui_error(op, "failure to open '%s'", victim); + return FALSE; + } + + if (S_ISDIR(stat_buf.st_mode) == 0) { + ui_error(op, "%s is not a directory", victim); + return FALSE; + } + + if ((dir = opendir(victim)) == NULL) { + ui_error(op, "Failure reading directory %s", victim); + return FALSE; + } + + while ((ent = readdir(dir)) != NULL) { + + if (((strcmp(ent->d_name, ".")) == 0) || + ((strcmp(ent->d_name, "..")) == 0)) continue; + + len = strlen(victim) + strlen(ent->d_name) + 2; + filename = (char *) nvalloc(len); + snprintf(filename, len, "%s/%s", victim, ent->d_name); + + if (lstat(filename, &stat_buf) == -1) { + ui_error(op, "failure to open '%s'", filename); + free(filename); + return FALSE; + } + + if (S_ISDIR(stat_buf.st_mode)) { + remove_directory(op, filename); + } else { + if (unlink(filename) != 0) { + ui_error(op, "Failure removing file %s (%s)", + filename, strerror(errno)); + } + } + free(filename); + } + + if (rmdir(victim) != 0) { + ui_error(op, "Failure removing directory %s (%s)", + victim, strerror(errno)); + return FALSE; + } + + return TRUE; + +} /* remove_directory() */ + + + +/* + * touch_directory() - recursively touch all files (and directories) + * in the specified directory, bringing their access and modification + * times up to date. + */ + +int touch_directory(Options *op, const char *victim) +{ + struct stat stat_buf; + DIR *dir; + struct dirent *ent; + struct utimbuf time_buf; + char *filename; + + if (lstat(victim, &stat_buf) == -1) { + ui_error(op, "failure to open '%s'", victim); + return FALSE; + } + + if (S_ISDIR(stat_buf.st_mode) == 0) { + ui_error(op, "%s is not a directory", victim); + return FALSE; + } + + if ((dir = opendir(victim)) == NULL) { + ui_error(op, "Failure reading directory %s", victim); + return FALSE; + } + + /* get the current time */ + + time_buf.actime = time(NULL); + time_buf.modtime = time_buf.actime; + + /* loop over each entry in the directory */ + + while ((ent = readdir(dir)) != NULL) { + + if (((strcmp(ent->d_name, ".")) == 0) || + ((strcmp(ent->d_name, "..")) == 0)) continue; + + filename = nvstrcat(victim, "/", ent->d_name, NULL); + + /* stat the file to get the type */ + + if (lstat(filename, &stat_buf) == -1) { + ui_error(op, "failure to open '%s'", filename); + nvfree(filename); + return FALSE; + } + + /* if it is a directory, call this recursively */ + + if (S_ISDIR(stat_buf.st_mode)) { + if (!touch_directory(op, filename)) { + nvfree(filename); + return FALSE; + } + } + + /* finally, set the access and modification times */ + + if (utime(filename, &time_buf) != 0) { + ui_error(op, "Error setting modification time for %s", filename); + nvfree(filename); + return FALSE; + } + + nvfree(filename); + } + + if (closedir(dir) != 0) { + ui_error(op, "Error while closing directory %s.", victim); + return FALSE; + } + + return TRUE; + +} /* touch_directory() */ + + +/* + * copy_file() - copy the file specified by srcfile to dstfile, using + * mmap and memcpy. The destination file is created with the + * permissions specified by mode. Roughly based on code presented by + * Richard Stevens, in Advanced Programming in the Unix Environment, + * 12.9. + */ + +int copy_file(Options *op, const char *srcfile, + const char *dstfile, mode_t mode) +{ + int src_fd, dst_fd; + struct stat stat_buf; + char *src, *dst; + + if ((src_fd = open(srcfile, O_RDONLY)) == -1) { + ui_error (op, "Unable to open '%s' for copying (%s)", + srcfile, strerror (errno)); + goto fail; + } + if ((dst_fd = open(dstfile, O_RDWR | O_CREAT | O_TRUNC, mode)) == -1) { + ui_error (op, "Unable to create '%s' for copying (%s)", + dstfile, strerror (errno)); + goto fail; + } + if (fstat(src_fd, &stat_buf) == -1) { + ui_error (op, "Unable to determine size of '%s' (%s)", + srcfile, strerror (errno)); + goto fail; + } + if (lseek(dst_fd, stat_buf.st_size - 1, SEEK_SET) == -1) { + ui_error (op, "Unable to set file size for '%s' (%s)", + dstfile, strerror (errno)); + goto fail; + } + if (write(dst_fd, "", 1) != 1) { + ui_error (op, "Unable to write file size for '%s' (%s)", + dstfile, strerror (errno)); + goto fail; + } + if ((src = mmap(0, stat_buf.st_size, PROT_READ, + MAP_FILE | MAP_SHARED, src_fd, 0)) == (void *) -1) { + ui_error (op, "Unable to map source file '%s' for copying (%s)", + srcfile, strerror (errno)); + goto fail; + } + if ((dst = mmap(0, stat_buf.st_size, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, dst_fd, 0)) == (void *) -1) { + ui_error (op, "Unable to map destination file '%s' for copying (%s)", + dstfile, strerror (errno)); + goto fail; + } + + memcpy (dst, src, stat_buf.st_size); + + if (munmap (src, stat_buf.st_size) == -1) { + ui_error (op, "Unable to unmap source file '%s' after copying (%s)", + srcfile, strerror (errno)); + goto fail; + } + if (munmap (dst, stat_buf.st_size) == -1) { + ui_error (op, "Unable to unmap destination file '%s' after " + "copying (%s)", dstfile, strerror (errno)); + goto fail; + } + + /* + * the mode used to create dst_fd may have been affected by the + * user's umask; so explicitly set the mode again + */ + + fchmod(dst_fd, mode); + + close (src_fd); + close (dst_fd); + + return TRUE; + + fail: + return FALSE; + +} /* copy_file() */ + + + +/* + * write_temp_file() - write the given data to a temporary file, + * setting the file's permissions to those specified in perm. On + * success the name of the temporary file is returned; on error NULL + * is returned. + */ + +char *write_temp_file(Options *op, const int len, + const unsigned char *data, mode_t perm) +{ + unsigned char *dst = (void *) -1; + char *tmpfile = NULL; + int fd = -1; + int ret = FALSE; + + /* create a temporary file */ + + tmpfile = nvstrcat(op->tmpdir, "/nv-tmp-XXXXXX", NULL); + + fd = mkstemp(tmpfile); + if (fd == -1) { + ui_warn(op, "Unable to create temporary file (%s).", + strerror(errno)); + goto done; + } + + /* set the temporary file's size */ + + if (lseek(fd, len - 1, SEEK_SET) == -1) { + ui_warn(op, "Unable to set file size for temporary file (%s).", + strerror(errno)); + goto done; + } + if (write(fd, "", 1) != 1) { + ui_warn(op, "Unable to write file size for temporary file (%s).", + strerror(errno)); + goto done; + } + + /* mmap the temporary file */ + + if ((dst = mmap(0, len, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, 0)) == (void *) -1) { + ui_warn(op, "Unable to map temporary file (%s).", strerror(errno)); + goto done; + } + + /* copy the data out to the file */ + + memcpy(dst, data, len); + + /* set the desired permissions on the file */ + + if (fchmod(fd, perm) == -1) { + ui_warn(op, "Unable to set permissions %04o on temporary " + "file (%s)", perm, strerror(errno)); + goto done; + } + + ret = TRUE; + + done: + + /* unmap the temporary file */ + + if (dst != (void *) -1) { + if (munmap(dst, len) == -1) { + ui_warn(op, "Unable to unmap temporary file (%s).", + strerror(errno)); + } + } + + /* close the temporary file */ + + if (fd != -1) close(fd); + + if (ret) { + return tmpfile; + } else { + if (tmpfile) nvfree(tmpfile); + return NULL; + } + +} /* write_temp_file() */ + + + +/* + * select_tls_class() - determine which tls class should be installed + * on the user's machine; if tls_test() fails, just install the + * classic tls libraries. If tls_test() passes, install both OpenGL + * sets, but only the new tls libglx. + */ + +void select_tls_class(Options *op, Package *p) +{ + int i; + + if (!tls_test(op, FALSE)) { + + /* + * tls libraries will not run on this system; just install the + * classic OpenGL libraries: clear the FILE_TYPE of any + * FILE_CLASS_NEW_TLS package entries. + */ + + ui_log(op, "Installing classic TLS OpenGL libraries."); + + for (i = 0; i < p->num_entries; i++) { + if (p->entries[i].flags & FILE_CLASS_NEW_TLS) { + p->entries[i].flags &= ~FILE_TYPE_MASK; + p->entries[i].dst = NULL; + } + } + } else { + + /* + * tls libraries will run on this system: install both the + * classic and new TLS libraries. + */ + + ui_log(op, "Installing both new and classic TLS OpenGL libraries."); + } + +#if defined(NV_X86_64) + + /* + * If we are installing on amd64, then we need to perform a + * similar test for the 32bit compatibility libraries + */ + + if (!tls_test(op, TRUE)) { + + /* + * 32bit tls libraries will not run on this system; just + * install the classic OpenGL libraries: clear the FILE_TYPE + * of any FILE_CLASS_NEW_TLS_32 package entries. + */ + + ui_log(op, "Installing classic TLS 32bit OpenGL libraries."); + + for (i = 0; i < p->num_entries; i++) { + if (p->entries[i].flags & FILE_CLASS_NEW_TLS_32) { + p->entries[i].flags &= ~FILE_TYPE_MASK; + p->entries[i].dst = NULL; + } + } + } else { + + /* + * 32bit tls libraries will run on this system: install both + * the classic and new TLS libraries. + */ + + ui_log(op, "Installing both new and classic TLS 32bit " + "OpenGL libraries."); + } + +#endif /* NV_X86_64 */ + +} /* select_tls_class() */ + + +/* + * set_destinations() - given the Options and Package structures, + * assign the destination field in each Package entry, building from + * the OpenGL and XFree86 prefixes, the path relative to the prefix, + * and the filename. This assumes that the prefixes have already been + * assigned in the Options struct. + */ + +int set_destinations(Options *op, Package *p) +{ + char *prefix, *path, *name; + int i; + + for (i = 0; i < p->num_entries; i++) { + + switch (p->entries[i].flags & FILE_TYPE_MASK) { + + case FILE_TYPE_KERNEL_MODULE_CMD: + case FILE_TYPE_KERNEL_MODULE_SRC: + + /* we don't install kernel module sources */ + + p->entries[i].dst = NULL; + continue; + + case FILE_TYPE_OPENGL_LIB: + case FILE_TYPE_OPENGL_SYMLINK: + prefix = op->opengl_prefix; + path = p->entries[i].path; + break; + + case FILE_TYPE_XFREE86_LIB: + case FILE_TYPE_XFREE86_SYMLINK: + prefix = op->xfree86_prefix; + path = p->entries[i].path; + break; + + /* + * XXX should the OpenGL headers and documentation also go + * under the OpenGL installation prefix? The Linux OpenGL + * ABI requires that the header files be installed in + * /usr/include/GL/. + */ + + case FILE_TYPE_OPENGL_HEADER: + prefix = op->opengl_prefix; + path = OPENGL_HEADER_DST_PATH; + break; + + case FILE_TYPE_DOCUMENTATION: + prefix = op->opengl_prefix; + path = p->entries[i].path; + break; + + case FILE_TYPE_INSTALLER_BINARY: + prefix = op->installer_prefix; + path = INSTALLER_BINARY_DST_PATH; + break; + + case FILE_TYPE_UTILITY_BINARY: + prefix = op->utility_prefix; + path = UTILITY_BINARY_DST_PATH; + break; + + case FILE_TYPE_KERNEL_MODULE: + + /* + * the kernel module dst field has already been + * initialized in add_kernel_module_to_package() + */ + + continue; + + default: + + /* + * silently ignore anything that doesn't match; libraries + * of the wrong TLS class may fall in here, for example. + */ + + p->entries[i].dst = NULL; + continue; + } + + name = p->entries[i].name; + + p->entries[i].dst = nvstrcat(prefix, "/", path, "/", name, NULL); + } + + return TRUE; + +} /* set_destinations() */ + + + +/* + * get_license_acceptance() - stat the license file to find out its + * length, open the file, mmap it, and pass it to the ui for + * acceptance. + */ + +int get_license_acceptance(Options *op) +{ + struct stat buf; + char *text; + int fd; + + /* trivial accept if the user accepted on the command line */ + + if (op->accept_license) { + ui_log(op, "License accepted by command line option."); + return TRUE; + } + + if ((fd = open(LICENSE_FILE, 0x0)) == -1) goto failed; + + if (fstat(fd, &buf) != 0) goto failed; + + if ((text = (char *) mmap(NULL, buf.st_size, PROT_READ, + MAP_FILE|MAP_SHARED, + fd, 0x0)) == (char *) -1) goto failed; + + if (!ui_display_license(op, text)) { + ui_message(op, "License not accepted. Aborting installation."); + munmap(text, buf.st_size); + close(fd); + return FALSE; + } + + ui_log(op, "License accepted."); + + munmap(text, buf.st_size); + close(fd); + + return TRUE; + + failed: + + ui_error(op, "Unable to open License file '%s' (%s)", + LICENSE_FILE, strerror(errno)); + return FALSE; + +} /* get_license_acceptance() */ + + + +/* + * get_prefixes() - if in expert mode, ask the user for the OpenGL and + * XFree86 installation prefix. The default prefixes are already set + * in parse_commandline(). + */ + +int get_prefixes (Options *op) +{ + char *ret; + + if (op->expert) { + ret = ui_get_input(op, op->xfree86_prefix, + "X installation prefix (only under " + "rare circumstances should this be changed " + "from the default)"); + if (ret && ret[0]) { + op->xfree86_prefix = ret; + if (!confirm_path(op, op->xfree86_prefix)) return FALSE; + } + } + + remove_trailing_slashes(op->xfree86_prefix); + ui_expert(op, "X installation prefix is: '%s'", op->xfree86_prefix); + + if (op->expert) { + ret = ui_get_input(op, op->opengl_prefix, + "OpenGL installation prefix (only under " + "rare circumstances should this be changed " + "from the default)"); + if (ret && ret[0]) { + op->opengl_prefix = ret; + if (!confirm_path(op, op->opengl_prefix)) return FALSE; + } + } + + remove_trailing_slashes(op->opengl_prefix); + ui_expert(op, "OpenGL installation prefix is: '%s'", op->opengl_prefix); + + if (op->expert) { + ret = ui_get_input(op, op->installer_prefix, + "Installer installation prefix"); + if (ret && ret[0]) { + op->installer_prefix = ret; + if (!confirm_path(op, op->installer_prefix)) return FALSE; + } + } + + remove_trailing_slashes(op->installer_prefix); + ui_expert(op, "Installer installation prefix is: '%s'", + op->installer_prefix); + + return TRUE; + +} /* get_prefixes() */ + + + +/* + * add_kernel_module_to_package() - append the kernel module + * (contained in p->kernel_module_build_directory) to the package list + * for installation. + */ + +int add_kernel_module_to_package(Options *op, Package *p) +{ + int n, len; + + n = p->num_entries; + + p->entries = + (PackageEntry *) nvrealloc(p->entries, (n + 1) * sizeof(PackageEntry)); + + len = strlen(p->kernel_module_build_directory) + + strlen(p->kernel_module_filename) + 2; + p->entries[n].file = (char *) nvalloc(len); + snprintf(p->entries[n].file, len, "%s/%s", + p->kernel_module_build_directory, p->kernel_module_filename); + + p->entries[n].path = NULL; + p->entries[n].target = NULL; + p->entries[n].flags = FILE_TYPE_KERNEL_MODULE; + p->entries[n].mode = 0644; + + p->entries[n].name = strrchr(p->entries[n].file, '/'); + if (p->entries[n].name) p->entries[n].name++; + if (!p->entries[n].name) p->entries[n].name = p->entries[n].file; + + len = strlen(op->kernel_module_installation_path) + + strlen(p->kernel_module_filename) + 2; + p->entries[n].dst = (char *) nvalloc(len); + snprintf (p->entries[n].dst, len, "%s/%s", + op->kernel_module_installation_path, p->kernel_module_filename); + + p->num_entries++; + + return TRUE; + +} /* add_kernel_module_to_package() */ + + + +/* + * remove_non_kernel_module_files_from_package() - clear the + * FILE_TYPE_MASK bits for each package entry that is not of type + * FILE_TYPE_KERNEL_MODULE + */ + +void remove_non_kernel_module_files_from_package(Options *op, Package *p) +{ + int i; + unsigned int flags; + + for (i = 0; i < p->num_entries; i++) { + flags = p->entries[i].flags & FILE_TYPE_MASK; + if ((flags != FILE_TYPE_KERNEL_MODULE) && + (flags != FILE_TYPE_KERNEL_MODULE_CMD)) + p->entries[i].flags &= ~FILE_TYPE_MASK; + } + +} /* remove_non_kernel_module_files_from_package() */ + + + +/* + * remove_trailing_slashes() - begin at the end of the given string, + * and overwrite slashes with NULL as long as we find slashes. + */ + +void remove_trailing_slashes(char *s) +{ + int len = strlen(s); + + while (s[len-1] == '/') s[--len] = '\0'; + +} /* remove_trailing_slashes() */ + + + +/* + * mode_string_to_mode() - convert the string s + */ + +int mode_string_to_mode(Options *op, char *s, mode_t *mode) +{ + char *endptr; + long int ret; + + ret = strtol(s, &endptr, 8); + + if ((ret == LONG_MIN) || (ret == LONG_MAX) || (*endptr != '\0')) { + ui_error(op, "Error parsing permission string '%s' (%s)", + s, strerror (errno)); + return FALSE; + } + + *mode = (mode_t) ret; + + return TRUE; + +} /* mode_string_to_mode() */ + + + +/* + * mode_to_permission_string() - given a mode bitmask, allocate and + * write a permission string. + */ + +char *mode_to_permission_string(mode_t mode) +{ + char *s = (char *) nvalloc(10); + memset (s, '-', 9); + + if (mode & (1 << 8)) s[0] = 'r'; + if (mode & (1 << 7)) s[1] = 'w'; + if (mode & (1 << 6)) s[2] = 'x'; + + if (mode & (1 << 5)) s[3] = 'r'; + if (mode & (1 << 4)) s[4] = 'w'; + if (mode & (1 << 3)) s[5] = 'x'; + + if (mode & (1 << 2)) s[6] = 'r'; + if (mode & (1 << 1)) s[7] = 'w'; + if (mode & (1 << 0)) s[8] = 'x'; + + s[9] = '\0'; + return s; + +} /* mode_to_permission_string() */ + + + +/* + * directory_exists() - + */ + +int directory_exists(Options *op, const char *dir) +{ + struct stat stat_buf; + + if ((stat (dir, &stat_buf) == -1) || (!S_ISDIR(stat_buf.st_mode))) { + return FALSE; + } else { + return TRUE; + } +} /* directory_exists() */ + + + +/* + * confirm_path() - check that the path exists; if not, ask the user + * if it's OK to create it and then do mkdir(). + * + * XXX for a while, I had thought that it would be better for this + * function to only ask the user if it was OK to create the directory; + * there are just too many reasons why mkdir might fail, though, so + * it's better to do mkdir() so that we can fail at this point in the + * installation. + */ + +int confirm_path(Options *op, const char *path) +{ + /* return TRUE if the path already exists and is a directory */ + + if (directory_exists(op, path)) return TRUE; + + if (ui_yes_no(op, TRUE, "The directory '%s' does not exist; " + "create?", path)) { + if (mkdir_recursive(op, path, 0755)) { + return TRUE; + } else { + return FALSE; + } + } + + ui_message(op, "Not creating directory '%s'; aborting installation.", + path); + + return FALSE; + +} /* confirm_path() */ + + + +/* + * mkdir_recursive() - create the path specified, also creating parent + * directories as needed; this is equivalent to `mkdir -p` + */ + +int mkdir_recursive(Options *op, const char *path, const mode_t mode) +{ + char *c, *tmp, ch; + + if (!path || !path[0]) return FALSE; + + tmp = nvstrdup(path); + remove_trailing_slashes(tmp); + + c = tmp; + do { + c++; + if ((*c == '/') || (*c == '\0')) { + ch = *c; + *c = '\0'; + if (!directory_exists(op, tmp)) { + if (mkdir(tmp, mode) != 0) { + ui_error(op, "Failure creating directory '%s': (%s)", + tmp, strerror(errno)); + free(tmp); + return FALSE; + } + } + *c = ch; + } + } while (*c); + + free(tmp); + return TRUE; + +} /* mkdir_recursive() */ + + + +/* + * get_symlink_target() - get the target of the symbolic link + * 'filename'. On success, a newly malloced string containing the + * target is returned. On error, an error message is printed and NULL + * is returned. + */ + +char *get_symlink_target(Options *op, const char *filename) +{ + struct stat stat_buf; + int ret, len = 0; + char *buf = NULL; + + if (lstat(filename, &stat_buf) == -1) { + ui_error(op, "Unable to get file properties for '%s' (%s).", + filename, strerror(errno)); + return NULL; + } + + if (!S_ISLNK(stat_buf.st_mode)) { + ui_error(op, "File '%s' is not a symbolic link.", filename); + return NULL; + } + + /* + * grow the buffer to be passed into readlink(2), until the buffer + * is big enough to hold the whole target. + */ + + do { + len += NV_LINE_LEN; + if (buf) free(buf); + buf = nvalloc(len); + ret = readlink(filename, buf, len - 1); + if (ret == -1) { + ui_error(op, "Failure while reading target of symbolic " + "link %s (%s).", filename, strerror(errno)); + free(buf); + return NULL; + } + } while (ret >= (len - 1)); + + buf[ret] = '\0'; + + return buf; + +} /* get_symlink_target() */ + + + +/* + * install_file() - install srcfile as dstfile; this is done by + * extracting the directory portion of dstfile, and then calling + * copy_file(). + */ + +int install_file(Options *op, const char *srcfile, + const char *dstfile, mode_t mode) +{ + int retval; + char *dirc, *dname; + + dirc = nvstrdup(dstfile); + dname = dirname(dirc); + + if (!mkdir_recursive(op, dname, 0755)) { + free(dirc); + return FALSE; + } + + retval = copy_file(op, srcfile, dstfile, mode); + free(dirc); + + return retval; + +} /* install_file() */ + + + +size_t get_file_size(Options *op, const char *filename) +{ + struct stat stat_buf; + + if (stat(filename, &stat_buf) == -1) { + ui_error(op, "Unable to determine file size of '%s' (%s).", + filename, strerror(errno)); + return 0; + } + + return stat_buf.st_size; + +} /* get_file_size() */ + + + +size_t fget_file_size(Options *op, const int fd) +{ + struct stat stat_buf; + + if (fstat(fd, &stat_buf) == -1) { + ui_error(op, "Unable to determine file size of file " + "descriptor %d (%s).", fd, strerror(errno)); + return 0; + } + + return stat_buf.st_size; + +} /* fget_file_size() */ + + + +char *get_tmpdir(Options *op) +{ + char *tmpdirs[] = { NULL, "/tmp", ".", NULL }; + int i; + + tmpdirs[0] = getenv("TMPDIR"); + tmpdirs[3] = getenv("HOME"); + + for (i = 0; i < 4; i++) { + if (tmpdirs[i] && directory_exists(op, tmpdirs[i])) { + return (tmpdirs[i]); + } + } + + return NULL; + +} /* get_tmpdir() */ + + + +/* + * make_tmpdir() - create a temporary directory; XXX we should really + * use mkdtemp, but it is not available on all systems. + */ + +char *make_tmpdir(Options *op) +{ + char tmp[32], *tmpdir; + + snprintf(tmp, 32, "%d", getpid()); + + tmpdir = nvstrcat(op->tmpdir, "/nvidia-", tmp, NULL); + + if (directory_exists(op, tmpdir)) { + remove_directory(op, tmpdir); + } + + if (!mkdir_recursive(op, tmpdir, 0655)) { + return NULL; + } + + return tmpdir; + +} /* make_tmpdir() */ + + + +/* + * nvrename() - replacement for rename(2), because rename(2) can't + * cross filesystem boundaries. Get the src file attributes, copy the + * src file to the dst file, stamp the dst file with the src file's + * timestamp, and delete the src file. Returns FALSE on error, TRUE + * on success. + */ + +int nvrename(Options *op, const char *src, const char *dst) +{ + struct stat stat_buf; + struct utimbuf utime_buf; + + if (stat(src, &stat_buf) == -1) { + ui_error(op, "Unable to determine file attributes of file " + "%s (%s).", src, strerror(errno)); + return FALSE; + } + + if (!copy_file(op, src, dst, stat_buf.st_mode)) return FALSE; + + utime_buf.actime = stat_buf.st_atime; /* access time */ + utime_buf.modtime = stat_buf.st_mtime; /* modification time */ + + if (utime(dst, &utime_buf) == -1) { + ui_warn(op, "Unable to transfer timestamp from '%s' to '%s' (%s).", + src, dst, strerror(errno)); + } + + if (unlink(src) == -1) { + ui_error(op, "Unable to delete '%s' (%s).", src, strerror(errno)); + return FALSE; + } + + return TRUE; + +} /* nvrename() */ + + + +/* + * check_for_existing_rpms() - check if any of the previous NVIDIA + * rpms are installed on the system. If we find any, ask the user if + * we may remove them. + */ + +int check_for_existing_rpms(Options *op) +{ + /* list of rpms to remove; should be in dependency order */ + + const char *rpms[2] = { "NVIDIA_GLX", "NVIDIA_kernel" }; + + char *data, *cmd; + int i, ret; + + if (op->no_rpms) { + ui_log(op, "Skipping check for conflicting rpms."); + return TRUE; + } + + for (i = 0; i < 2; i++) { + + cmd = nvstrcat("env LD_KERNEL_ASSUME=2.2.5 rpm --query ", + rpms[i], NULL); + ret = run_command(op, cmd, NULL, FALSE, 0, TRUE); + nvfree(cmd); + + if (ret == 0) { + if (!ui_yes_no(op, TRUE, "An %s rpm appears to already be " + "installed on your system. As part of installing " + "the new driver, this %s rpm will be uninstalled. " + "Are you sure you want to continue? ('no' will " + "abort installation)", rpms[i], rpms[i])) { + ui_log(op, "Installation aborted."); + return FALSE; + } + + cmd = nvstrcat("rpm --erase --nodeps ", rpms[i], NULL); + ret = run_command(op, cmd, &data, op->expert, 0, TRUE); + nvfree(cmd); + + if (ret == 0) { + ui_log(op, "Removed %s.", rpms[i]); + } else { + ui_warn(op, "Unable to erase %s rpm: %s", rpms[i], data); + } + + nvfree(data); + } + } + + return TRUE; + +} /* check_for_existing_rpms() */ + + + +/* + * copy_directory_contents() - copy the contents of directory src to + * directory dst. This only copies files; subdirectories are ignored. + */ + +int copy_directory_contents(Options *op, const char *src, const char *dst) +{ + DIR *dir; + struct dirent *ent; + char *srcfile, *dstfile; + struct stat stat_buf; + + if ((dir = opendir(src)) == NULL) { + ui_error(op, "Unable to open directory '%s' (%s).", + src, strerror(errno)); + return FALSE; + } + + while ((ent = readdir(dir)) != NULL) { + + if (((strcmp(ent->d_name, ".")) == 0) || + ((strcmp(ent->d_name, "..")) == 0)) continue; + + srcfile = nvstrcat(src, "/", ent->d_name, NULL); + + /* only copy regular files */ + + if ((stat(srcfile, &stat_buf) == -1) || !(S_ISREG(stat_buf.st_mode))) { + nvfree(srcfile); + continue; + } + + dstfile = nvstrcat(dst, "/", ent->d_name, NULL); + + if (!copy_file(op, srcfile, dstfile, stat_buf.st_mode)) return FALSE; + + nvfree(srcfile); + nvfree(dstfile); + } + + if (closedir(dir) != 0) { + ui_error(op, "Failure while closing directory '%s' (%s).", + src, strerror(errno)); + + return FALSE; + } + + return TRUE; + +} /* copy_directory_contents() */ + + + +/* + * pack_precompiled_kernel_interface() - + */ + +int pack_precompiled_kernel_interface(Options *op, Package *p) +{ + char *cmd, time_str[256], *proc_version_string; + char major[16], minor[16], patch[16]; + char *result, *descr; + time_t t; + struct utsname buf; + int ret; + + ui_log(op, "Packaging precompiled kernel interface."); + + /* make sure the precompiled_kernel_interface_directory exists */ + + mkdir_recursive(op, p->precompiled_kernel_interface_directory, 0755); + + /* use the time in the output string... should be fairly unique */ + + t = time(NULL); + snprintf(time_str, 256, "%lu", t); + + /* read the proc version string */ + + proc_version_string = read_proc_version(op); + + /* get the version strings */ + + snprintf(major, 16, "%d", p->major); + snprintf(minor, 16, "%d", p->minor); + snprintf(patch, 16, "%d", p->patch); + + /* use the uname string as the description */ + + uname(&buf); + descr = nvstrcat(buf.sysname, " ", + buf.release, " ", + buf.version, " ", + buf.machine, NULL); + + /* build the mkprecompiled command */ + + cmd = nvstrcat("./usr/bin/mkprecompiled --interface=", + p->kernel_module_build_directory, "/", + PRECOMPILED_KERNEL_INTERFACE_FILENAME, + " --output=", p->precompiled_kernel_interface_directory, + "/", PRECOMPILED_KERNEL_INTERFACE_FILENAME, + "-", p->version_string, ".", time_str, + " --description=\"", descr, "\"", + " --proc-version=\"", proc_version_string, "\"", + " --major=", major, + " --minor=", minor, + " --patch=", patch, NULL); + + /* execute the command */ + + ret = run_command(op, cmd, &result, FALSE, 0, TRUE); + + nvfree(cmd); + nvfree(proc_version_string); + nvfree(descr); + + /* remove the old kernel interface file */ + + cmd = nvstrcat(p->kernel_module_build_directory, "/", + PRECOMPILED_KERNEL_INTERFACE_FILENAME, NULL); + + unlink(cmd); /* XXX what to do if this fails? */ + + nvfree(cmd); + + if (ret != 0) { + ui_error(op, "Unable to package precompiled kernel interface: %s", + result); + } + + nvfree(result); + + if (ret == 0) return TRUE; + else return FALSE; + +} /* pack_kernel_interface() */ + + + +/* + * nv_strreplace() - we can't assume that the user has sed installed + * on their system, so use this function to preform simple string + * search and replacement. Returns a newly allocated string that is a + * duplicate of src, with all instances of 'orig' replaced with + * 'replace'. + */ + +static char *nv_strreplace(char *src, char *orig, char *replace) +{ + char *prev_s, *end_s, *s; + char *d, *dst; + int len, dst_len, orig_len, replace_len; + int done = 0; + + prev_s = s = src; + end_s = src + strlen(src) + 1; + + dst = NULL; + dst_len = 0; + + orig_len = strlen(orig); + replace_len = strlen(replace); + + do { + /* find the next instances of orig in src */ + + s = strstr(prev_s, orig); + + /* + * if no match, then flag that we are done once we finish + * copying the src into dst + */ + + if (!s) { + s = end_s; + done = 1; + } + + /* copy the characters between prev_s and s into dst */ + + len = s - prev_s; + dst = realloc(dst, dst_len + len + 1); + d = dst + dst_len; + strncpy(d, prev_s, len); + d[len] = '\0'; + dst_len += len; + + /* if we are not done, then append the replace string */ + + if (!done) { + dst = realloc(dst, dst_len + replace_len + 1); + d = dst + dst_len; + strncpy(d, replace, replace_len); + d[replace_len] = '\0'; + dst_len += replace_len; + } + + /* skip past the orig string */ + + if (!done) prev_s = s + orig_len; + + } while (!done); + + return dst; + +} /* nv_strreplace() */ + + + +/* + * process_libGL_la_files() - for any libGL.la files in the package, + * copy them to a temporary file, replacing __GENERATED_BY__ and + * __LIBGL_PATH__ as appropriate. Then, add the new file to the + * package list. + */ + +void process_libGL_la_files(Options *op, Package *p) +{ + int i, n, src_fd, dst_fd, len; + struct stat stat_buf; + char *src, *dst, *tmp, *tmp0, *tmp1, *tmpfile; + + for (i = 0; i < p->num_entries; i++) { + if (p->entries[i].flags & FILE_TYPE_LIBGL_LA) { + + src_fd = dst_fd = -1; + tmp0 = tmp1 = src = dst = tmpfile = NULL; + len = 0; + + /* open the file */ + + if ((src_fd = open(p->entries[i].file, O_RDONLY)) == -1) { + ui_error(op, "Unable to open '%s' for copying (%s)", + p->entries[i].file, strerror(errno)); + goto done; + } + + /* get the size of the file */ + + if (fstat(src_fd, &stat_buf) == -1) { + ui_error(op, "Unable to determine size of '%s' (%s)", + p->entries[i].file, strerror(errno)); + goto done; + } + + /* mmap the file */ + + if ((src = mmap(0, stat_buf.st_size, PROT_READ, + MAP_FILE|MAP_SHARED, src_fd, 0)) == (void *) -1) { + ui_error (op, "Unable to map source file '%s' for " + "copying (%s)", p->entries[i].file, strerror(errno)); + src = NULL; + goto done; + } + + if (!src) { + ui_log(op, "%s is empty; skipping.", p->entries[i].file); + goto done; + } + + /* replace __LIBGL_PATH__ */ + + tmp = nvstrcat(op->opengl_prefix, "/", p->entries[i].path, NULL); + tmp0 = nv_strreplace(src, "__LIBGL_PATH__", tmp); + nvfree(tmp); + + /* replace __GENERATED_BY__ */ + + tmp = nvstrcat(PROGRAM_NAME, ": ", NVIDIA_INSTALLER_VERSION, NULL); + tmp1 = nv_strreplace(tmp0, "__GENERATED_BY__", tmp); + nvfree(tmp); + + /* create a temporary file to store the processed libGL.la file */ + + tmpfile = nvstrcat(op->tmpdir, "/libGL.la-XXXXXX", NULL); + if ((dst_fd = mkstemp(tmpfile)) == -1) { + ui_error(op, "Unable to create temporary file (%s)", + strerror(errno)); + goto done; + } + + /* set the size of the new file */ + + len = strlen(tmp1); + + if (lseek(dst_fd, len - 1, SEEK_SET) == -1) { + ui_error(op, "Unable to set file size for '%s' (%s)", + tmpfile, strerror(errno)); + goto done; + } + if (write(dst_fd, "", 1) != 1) { + ui_error(op, "Unable to write file size for '%s' (%s)", + tmpfile, strerror(errno)); + goto done; + } + + /* mmap the new file */ + + if ((dst = mmap(0, len, PROT_READ | PROT_WRITE, + MAP_FILE|MAP_SHARED, dst_fd, 0)) == (void *) -1) { + ui_error(op, "Unable to map destination file '%s' for " + "copying (%s)", tmpfile, strerror(errno)); + dst = NULL; + goto done; + } + + /* write the data out to the new file */ + + memcpy(dst, tmp1, len); + + /* add this new file to the package */ + + n = p->num_entries; + + p->entries = + (PackageEntry *) nvrealloc(p->entries, + (n + 1) * sizeof(PackageEntry)); + p->entries[n].file = tmpfile; + p->entries[n].path = p->entries[i].path; + p->entries[n].target = NULL; + p->entries[n].flags = ((p->entries[i].flags & FILE_CLASS_MASK) | + FILE_TYPE_OPENGL_LIB); + p->entries[n].mode = p->entries[i].mode; + + p->entries[n].name = nvstrdup(p->entries[i].name); + + p->num_entries++; + + done: + + if (src) { + if (munmap(src, stat_buf.st_size) == -1) { + ui_error(op, "Unable to unmap source file '%s' after " + "copying (%s)", p->entries[i].file, + strerror(errno)); + } + } + + if (dst) { + if (munmap(dst, len) == -1) { + ui_error (op, "Unable to unmap destination file '%s' " + "after copying (%s)", tmpfile, strerror(errno)); + } + } + + if (src_fd != -1) close(src_fd); + if (dst_fd != -1) close(dst_fd); + + if (tmp0) nvfree(tmp0); + if (tmp1) nvfree(tmp1); + } + } +} /* process_libGL_la_files() */ @@ -0,0 +1,61 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + */ + +#ifndef __NVIDIA_INSTALLER_FILES_H__ +#define __NVIDIA_INSTALLER_FILES_H__ + +#include "nvidia-installer.h" + +int remove_directory(Options *op, const char *victim); +int touch_directory(Options *op, const char *victim); +int copy_file(Options *op, const char *srcfile, + const char *dstfile, mode_t mode); +char *write_temp_file(Options *op, const int len, + const unsigned char *data, mode_t perm); +void select_tls_class(Options *op, Package *p); /* XXX move? */ +int set_destinations(Options *op, Package *p); /* XXX move? */ +int get_license_acceptance(Options *op); /* XXX move? */ +int get_prefixes(Options *op); /* XXX move? */ +int add_kernel_module_to_package(Options *op, Package *p); +void remove_non_kernel_module_files_from_package(Options *op, Package *p); +void remove_trailing_slashes(char *s); +int mode_string_to_mode(Options *op, char *s, mode_t *mode); +char *mode_to_permission_string(mode_t mode); +int directory_exists(Options *op, const char *dir); +int confirm_path(Options *op, const char *path); +int mkdir_recursive(Options *op, const char *path, const mode_t mode); +char *get_symlink_target(Options *op, const char *filename); +int install_file(Options *op, const char *srcfile, + const char *dstfile, mode_t mode); +size_t get_file_size(Options *op, const char *filename); +size_t fget_file_size(Options *op, const int fd); +char *get_tmpdir(Options *op); +char *make_tmpdir(Options *op); +int nvrename(Options *op, const char *src, const char *dst); +int check_for_existing_rpms(Options *op); +int copy_directory_contents(Options *op, const char *src, const char *dst); +int pack_precompiled_kernel_interface(Options *op, Package *p); +void process_libGL_la_files(Options *op, Package *p); + +#endif /* __NVIDIA_INSTALLER_FILES_H__ */ diff --git a/format.c b/format.c new file mode 100644 index 0000000..758d9cf --- /dev/null +++ b/format.c @@ -0,0 +1,179 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * format.c - this source file contains routines for formatting string + * output. + */ + + +#include <stdio.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <stdlib.h> + +#include "nvidia-installer.h" +#include "format.h" +#include "misc.h" + +static unsigned short __terminal_width = 0; + + + +#define DEFAULT_WIDTH 75 + +/* + * reset_current_terminal_width() - if new_val is zero, then use the + * TIOCGWINSZ ioctl to get the current width of the terminal, and + * assign it the value to __terminal_width. If the ioctl fails, use a + * hardcoded constant. If new_val is non-zero, then use new_val. + */ + +void reset_current_terminal_width(unsigned short new_val) +{ + struct winsize ws; + + if (new_val) { + __terminal_width = new_val; + return; + } + + if (ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { + __terminal_width = DEFAULT_WIDTH; + } else { + __terminal_width = ws.ws_col - 1; + } +} /* get_current_terminal_width() */ + + + +/* + * fmtout() - stdout format function: prints the given string to + * stdout with no prefix. + */ + +void fmtout(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vformat(stdout, TRUE, NULL, fmt, ap); + va_end(ap); + +} /* fmtout() */ + + + +/* + * fmtoutp() - stdout format function with prefix: prints the given + * string to stdout with the given prefix. + */ + +void fmtoutp(const char *prefix, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vformat(stdout, TRUE, prefix, fmt, ap); + va_end(ap); + +} /* fmtoutp() */ + + + +/* + * fmterr() - stderr format function: prints the given string to + * stderr with no prefix. + */ + +void fmterr(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vformat(stderr, TRUE, NULL, fmt, ap); + va_end(ap); + +} /* fmterr() */ + + + +/* + * fmterrp() - stderr format function: prints the given string to + * stderr with the given prefix. + */ + +void fmterrp(const char *prefix, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vformat(stderr, TRUE, prefix, fmt, ap); + va_end(ap); + +} /* fmterrp() */ + + + +/* + * format() & vformat() - these takes a printf-style format string and + * a variable list of args. We use assemble_string to generate the + * desired string, and then call nv_format_text_rows() to format the + * string so that not more than __terminal_width characters are + * printed across. + * + * The resulting formatted output is written to the specified stream. + * The output may also include an optional prefix (to be prepended on + * the first line, and filled with spaces on subsequent lines. + * + * The wb argument indicates whether the line wrapping should only + * break on word boundaries. + */ + +void format(FILE *stream, const char *prefix, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vformat(stream, TRUE, prefix, fmt, ap); + va_end(ap); + +} /* format() */ + +void vformat(FILE *stream, const int wb, + const char *prefix, const char *fmt, va_list ap) +{ + char *buf; + int i; + TextRows *t; + + if (!__terminal_width) reset_current_terminal_width(0); + + buf = assemble_string(fmt, ap); + t = nv_format_text_rows(prefix, buf, __terminal_width, wb); + + for (i = 0; i < t->n; i++) fprintf(stream, "%s\n", t->t[i]); + + nv_free_text_rows(t); + free(buf); + +} /* vformat_b() */ diff --git a/format.h b/format.h new file mode 100644 index 0000000..a629470 --- /dev/null +++ b/format.h @@ -0,0 +1,43 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * format.h + */ + +#ifndef __NVIDIA_INSTALLER_FORMAT_H__ +#define __NVIDIA_INSTALLER_FORMAT_H__ + +#include <stdio.h> +#include <stdarg.h> + +void reset_current_terminal_width(unsigned short new_val); + +void fmtout(const char *fmt, ...); +void fmtoutp(const char *prefix, const char *fmt, ...); +void fmterr(const char *fmt, ...); +void fmterrp(const char *prefix, const char *fmt, ...); +void format(FILE *stream, const char *prefix, const char *fmt, ...); +void vformat(FILE *stream, const int wb, + const char *prefix, const char *fmt, va_list ap); + +#endif /* __NVIDIA_INSTALLER_FORMAT_H__ */ diff --git a/gen-ui-array.c b/gen-ui-array.c new file mode 100644 index 0000000..6cb7e26 --- /dev/null +++ b/gen-ui-array.c @@ -0,0 +1,94 @@ +/* + * This C program takes a filename and an array name, and prints to + * stdout an array of the data contained in the file. + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <sys/mman.h> + +int main(int argc, char *argv[]) +{ + char *arrayname, *filename; + unsigned char *src; + struct stat stat_buf; + int length, fd, i, n; + + if (argc != 3) { + fprintf(stderr, "usage: %s [filename] [arrayname]\n", argv[0]); + return 1; + } + + filename = argv[1]; + arrayname = argv[2]; + + /* open the file */ + + fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "unable to open(2) %s (%s)\n", + filename, strerror(errno)); + return 1; + } + + /* get the length of the file */ + + if (fstat(fd, &stat_buf) != 0) { + fprintf(stderr, "unable to stat(2) %s (%s)\n", + filename, strerror(errno)); + return 1; + } + + length = stat_buf.st_size; + + /* map the file */ + + src = mmap(0, length, PROT_READ, MAP_SHARED, fd, 0); + if (src == (void *) -1) { + fprintf(stderr, "unable to mmap(2) %s (%s)\n", + filename, strerror(errno)); + return 1; + } + + /* print the header */ + + printf("/*\n"); + printf( " * THIS FILE IS AUTOMATICALLY GENERATED - DO NOT EDIT\n"); + printf( " *\n"); + printf( " * This file contains the contents of the file \"%s\"\n", + filename); + printf( " * as the byte array %s[].\n", arrayname); + printf( " */\n\n"); + + /* print a separate variable that stores the size */ + + printf("const int %s_size = %d;\n\n", arrayname, length); + + /* print the array name */ + + printf("const unsigned char %s[%d] = {\n", arrayname, length); + + for (i = 0, n = 0; i < length; i++, n++) { + if (n == 0) printf(" "); + printf("0x%02x, ", src[i]); + if (n >= 11) { printf("\n"); n = -1; } + } + + printf("\n};\n\n"); + + /* unmap the file */ + + munmap(src, length); + + /* close the file */ + + close(fd); + + + return 0; +} diff --git a/install-from-cwd.c b/install-from-cwd.c new file mode 100644 index 0000000..6b78f60 --- /dev/null +++ b/install-from-cwd.c @@ -0,0 +1,653 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * install_from_cwd.c - + */ + + +#include <stdio.h> +#include <stdlib.h> + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "nvidia-installer.h" +#include "user-interface.h" +#include "kernel.h" +#include "command-list.h" +#include "backup.h" +#include "files.h" +#include "misc.h" +#include "sanity.h" + +/* local prototypes */ + + +static Package *parse_manifest(Options *op); + + + + +/* + * install_from_cwd() - perform an installation from the current + * working directory; we first ensure that we have a .manifest file in + * the cwd, and the files listed in the manifest exist and have + * correct checksums (to ensure the package hasn't been corrupted, not + * for anything security related). + * + * Second, make sure the user accepts the license. + * + * Then, optionally override the OpenGL and XFree86 installation + * prefixes. + * + * Determine the currently installed NVIDIA driver version (if any). + * + */ + +int install_from_cwd(Options *op) +{ + Package *p; + CommandList *c; + const char *msg; + + static const char edit_your_xf86config[] = + "Please update your XF86Config or xorg.conf file as " + "appropriate; see the file /usr/share/doc/" + "NVIDIA_GLX-1.0/README for details."; + + static const char suse_edit_your_xf86config[] = + "On SuSE Linux/United Linux please use SaX2 now to enable " + "the NVIDIA driver."; + + /* + * validate the manifest file in the cwd, and process it, building + * a Package struct + */ + + if ((p = parse_manifest(op)) == NULL) goto failed; + + ui_set_title(op, "%s (%d.%d-%d)", p->description, + p->major, p->minor, p->patch); + + /* make sure the kernel module is unloaded */ + + if (!check_for_unloaded_kernel_module(op, p)) goto failed; + + /* check that we are not running any X server */ + + if (!check_for_running_x(op)) goto failed; + + /* ask the user to accept the license */ + + if (!get_license_acceptance(op)) return FALSE; + + /* + * determine the current NVIDIA version (if any); ask the user if + * they really want to overwrite the existing installation + */ + + if (!check_for_existing_driver(op, p)) return FALSE; + + /* determine where to install the kernel module */ + + if (!determine_kernel_module_installation_path(op)) goto failed; + + /* + * do nvchooser-style logic to decide if we have a prebuilt kernel + * module for their kernel + * + * XXX One could make the argument that we should not actually do + * the building/linking now, but just add this to the list of + * operations and do it when we execute the operation list. I + * think it's better to make sure we have a kernel module early on + * -- a common problem for users will be not having a prebuilt + * kernel interface for their kernel, and not having the kernel + * headers installed, so it's better to catch that earlier on. + */ + + if (find_precompiled_kernel_interface(op, p)) { + + /* + * we have a prebuild kernel interface, so now link the kernel + * interface with the binary portion of the kernel module. + * + * XXX if linking fails, maybe we should fall through and + * attempt to build the kernel module? No, if linking fails, + * then there is something pretty seriously wrong... better to + * abort. + */ + + if (!link_kernel_module(op, p)) goto failed; + + } else { + + /* XXX we need to do a cc version check */ + + /* + * we do not have a prebuilt kernel interface; thus we'll need + * to compile the kernel interface, so determine where the + * kernel header files are + */ + + if (!determine_kernel_source_path(op)) goto failed; + + /* and now, build the kernel interface */ + + if (!build_kernel_module(op, p)) goto failed; + } + + /* + * if we got this far, we have a complete kernel module; test it + * to be sure it's OK + */ + + if (!test_kernel_module(op, p)) goto failed; + + /* add the kernel module to the list of things to install */ + + if (!add_kernel_module_to_package(op, p)) goto failed; + + /* + * if we are only installing the kernel module, then remove + * everything else from the package; otherwise do some + * OpenGL-specific stuff + */ + + if (op->kernel_module_only) { + remove_non_kernel_module_files_from_package(op, p); + } else { + + /* ask for the XFree86 and OpenGL installation prefixes. */ + + if (!get_prefixes(op)) goto failed; + + /* ask if we should install the OpenGL header files */ + + should_install_opengl_headers(op, p); + + /* + * select the appropriate TLS class, modifying the package as + * necessary. + */ + + select_tls_class(op, p); + + /* + * if the package contains any libGL.la files, process them + * (perform some search and replacing so that they reflect the + * correct installation path, etc) and add them to the + * packagelist to be installed. + */ + + process_libGL_la_files(op, p); + } + + /* + * now that we have the installation prefixes, build the + * destination for each file to be installed + */ + + if (!set_destinations(op, p)) goto failed; + + /* + * uninstall the existing driver; this needs to be done before + * building the command list. + * + * XXX if we uninstall now, then build the command list, and + * then ask the user if they really want to execute the + * command list, if the user decides not to execute the + * command list, they'll be left with no driver installed. + */ + + if (!op->kernel_module_only) { + if (!uninstall_existing_driver(op, FALSE)) goto failed; + } + + /* build a list of operations to execute to do the install */ + + if ((c = build_command_list(op, p)) == NULL) goto failed; + + /* call the ui to get approval for the list of commands */ + + if (!ui_approve_command_list(op, c, p->description)) return FALSE; + + /* initialize the backup log file */ + + if (!op->kernel_module_only) { + if (!init_backup(op, p)) goto failed; + } + + /* execute the command list */ + + if (!do_install(op, p, c)) goto failed; + + /* + * XXX IMPLEMENT ME: generate an XF86Config file? + */ + + /* + * check that everything is installed properly (post-install + * sanity check) + */ + + check_installed_files_from_package(op, p); + + if (!check_sysvipc(op)) goto failed; + + /* done */ + + if ((op->distro == SUSE) || (op->distro == UNITED_LINUX)) { + msg = suse_edit_your_xf86config; + } else { + msg = edit_your_xf86config; + } + + if (op->kernel_module_only) { + ui_message(op, "Installation of the kernel module for the %s " + "(version %s) is now complete.", + p->description, p->version_string); + } else { + ui_message(op, "Installation of the %s (version: %s) is now " + "complete. %s", p->description, p->version_string, msg); + } + + return TRUE; + + failed: + + if (op->logging) { + ui_error(op, "Installation has failed. Please see the file '%s' " + "for details. You may find suggestions on fixing " + "installation problems in the README available on the " + "Linux driver download page at www.nvidia.com.", + op->log_file_name); + } else { + ui_error(op, "Installation has failed. You may find suggestions " + "on fixing installation problems in the README available " + "on the Linux driver download page at www.nvidia.com."); + } + + return FALSE; + +} /* install_from_cwd() */ + + + +/* + * add_this_kernel() - build a precompiled kernel interface for the + * running kernel, and repackage the .run file to include the new + * precompiled kernel interface. + */ + +int add_this_kernel(Options *op) +{ + Package *p; + + /* parse the manifest */ + + if ((p = parse_manifest(op)) == NULL) goto failed; + + /* find the kernel header files */ + + if (!determine_kernel_source_path(op)) goto failed; + + /* build the precompiled kernel interface */ + + if (!build_kernel_interface(op, p)) goto failed; + + /* pack the precompiled kernel interface */ + + if (!pack_precompiled_kernel_interface(op, p)) goto failed; + + return TRUE; + + failed: + + ui_error(op, "Unable to add a precompiled kernel interface for the " + "running kernel."); + + return FALSE; + +} /* add_this_kernel() */ + + + +/* + * parse_manifest() - open and read the .manifest file in the current + * directory. + * + * The first nine lines of the .manifest file are: + * + * - a description string + * - a version string of the form "major.minor-patch" + * - the kernel module file name + * - the kernel interface file name + * - the kernel module name (what `rmmod` and `modprobe` should use) + * - a whitespace-separated list of module names that should be + * removed before installing a new kernel module + * - a whitespace-separated list of kernel module filenames that + * should be uninstalled before installing a new kernel module + * - kernel module build directory + * - directory containing precompiled kernel interfaces + * + * The rest of the manifest file is file entries. A file entry is a + * whitespace-separated list containing: + * + * - a filename (relative to the cwd) + * - an octal value describing the permissions + * - a flag describing the file type + * - certain file types will have a path + * - symbolic links will name the target of the link + */ + +static Package *parse_manifest (Options *op) +{ + char *buf, *c, *flag , *tmpstr; + int done, n, line; + int fd, len = 0; + struct stat stat_buf; + Package *p; + char *manifest = NULL, *ptr; + + p = (Package *) nvalloc(sizeof (Package)); + + /* open the manifest file */ + + if ((fd = open(".manifest", O_RDONLY)) == -1) { + ui_error(op, "No package found for installation. Please run " + "this utility with the '--help' option for usage " + "information."); + goto fail; + } + + if (fstat(fd, &stat_buf) == -1) goto cannot_open; + + len = stat_buf.st_size; + + manifest = mmap(0, len, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0); + if (manifest == (void *) -1) goto cannot_open; + + /* the first line is the description */ + + line = 1; + p->description = get_next_line(manifest, &ptr); + if (!p->description) goto invalid_manifest_file; + + /* the second line is the version */ + + line++; + p->version_string = get_next_line(ptr, &ptr); + if (!p->version_string) goto invalid_manifest_file; + if (!nvid_version(p->version_string, &p->major, &p->minor, &p->patch)) + goto invalid_manifest_file; + + /* the third line is the kernel module filename */ + + line++; + p->kernel_module_filename = get_next_line(ptr, &ptr); + if (!p->kernel_module_filename) goto invalid_manifest_file; + + /* + * XXX The kernel module filename in the .manifest file can no + * longer be used: the filename is different for 2.4 or 2.6 + * kernels. + */ + + free(p->kernel_module_filename); + p->kernel_module_filename = NULL; + + /* new fourth line is the kernel interface filename */ + + line++; + p->kernel_interface_filename = get_next_line(ptr, &ptr); + if (!p->kernel_interface_filename) goto invalid_manifest_file; + + /* the fifth line is the kernel module name */ + + line++; + p->kernel_module_name = get_next_line(ptr, &ptr); + if (!p->kernel_module_name) goto invalid_manifest_file; + + /* + * the sixth line is a whitespace-separated list of kernel modules + * to be unloaded before installing the new kernel module + */ + + line++; + tmpstr = get_next_line(ptr, &ptr); + if (!tmpstr) goto invalid_manifest_file; + + p->bad_modules = NULL; + c = tmpstr; + n = 0; + + do { + n++; + p->bad_modules = (char **) + nvrealloc(p->bad_modules, n * sizeof(char *)); + p->bad_modules[n-1] = read_next_word(c, &c); + } while (p->bad_modules[n-1]); + + /* + * the seventh line is a whitespace-separated list of kernel module + * filenames to be uninstalled before installing the new kernel + * module + */ + + line++; + tmpstr = get_next_line(ptr, &ptr); + if (!tmpstr) goto invalid_manifest_file; + + p->bad_module_filenames = NULL; + c = tmpstr; + n = 0; + + do { + n++; + p->bad_module_filenames = (char **) + nvrealloc(p->bad_module_filenames, n * sizeof(char *)); + p->bad_module_filenames[n-1] = read_next_word(c, &c); + } while (p->bad_module_filenames[n-1]); + + /* the eighth line is the kernel module build directory */ + + line++; + p->kernel_module_build_directory = get_next_line(ptr, &ptr); + if (!p->kernel_module_build_directory) goto invalid_manifest_file; + remove_trailing_slashes(p->kernel_module_build_directory); + + /* + * the nineth line is the directory containing precompiled kernel + * interfaces + */ + + line++; + p->precompiled_kernel_interface_directory = get_next_line(ptr, &ptr); + if (!p->precompiled_kernel_interface_directory) + goto invalid_manifest_file; + remove_trailing_slashes(p->precompiled_kernel_interface_directory); + + /* the rest of the file is file entries */ + + done = FALSE; + line++; + + do { + buf = get_next_line(ptr, &ptr); + if ((!buf) || (buf[0] == '\0')) { + done = TRUE; + } else { + + p->num_entries++; + n = p->num_entries - 1; + + /* extend the PackageEntry array */ + + if ((p->entries = (PackageEntry *) nvrealloc + (p->entries, sizeof(PackageEntry) * + p->num_entries)) == NULL) { + ui_error(op, "Memory allocation failure."); + goto fail; + } + + /* read the file name and permissions */ + + c = buf; + + p->entries[n].file = read_next_word(buf, &c); + tmpstr = read_next_word(c, &c); + + /* if any of them were NULL, fail */ + + if (!p->entries[n].file || !tmpstr) goto invalid_manifest_file; + + /* translate the mode string into an octal mode */ + + if (!mode_string_to_mode(op, tmpstr, &p->entries[n].mode)) { + goto invalid_manifest_file; + } + free(tmpstr); + + /* now, parse the flags */ + + p->entries[n].flags = 0x0; + + flag = read_next_word(c, &c); + if (!flag) goto invalid_manifest_file; + + if (strcmp(flag, "KERNEL_MODULE_SRC") == 0) + p->entries[n].flags |= FILE_TYPE_KERNEL_MODULE_SRC; + else if (strcmp(flag, "KERNEL_MODULE_CMD") == 0) + p->entries[n].flags |= FILE_TYPE_KERNEL_MODULE_CMD; + else if (strcmp(flag, "OPENGL_HEADER") == 0) + p->entries[n].flags |= FILE_TYPE_OPENGL_HEADER; + else if (strcmp(flag, "OPENGL_LIB") == 0) + p->entries[n].flags |= FILE_TYPE_OPENGL_LIB; + else if (strcmp(flag, "LIBGL_LA") == 0) + p->entries[n].flags |= FILE_TYPE_LIBGL_LA; + else if (strcmp(flag, "XFREE86_LIB") == 0) + p->entries[n].flags |= FILE_TYPE_XFREE86_LIB; + else if (strcmp(flag, "DOCUMENTATION") == 0) + p->entries[n].flags |= FILE_TYPE_DOCUMENTATION; + else if (strcmp(flag, "OPENGL_SYMLINK") == 0) + p->entries[n].flags |= FILE_TYPE_OPENGL_SYMLINK; + else if (strcmp(flag, "XFREE86_SYMLINK") == 0) + p->entries[n].flags |= FILE_TYPE_XFREE86_SYMLINK; + else if (strcmp(flag, "INSTALLER_BINARY") == 0) + p->entries[n].flags |= FILE_TYPE_INSTALLER_BINARY; + else if (strcmp(flag, "UTILITY_BINARY") == 0) + p->entries[n].flags |= FILE_TYPE_UTILITY_BINARY; + else if (strcmp(flag, "OPENGL_LIB_CLASSIC_TLS") == 0) + p->entries[n].flags |= (FILE_TYPE_OPENGL_LIB | + FILE_CLASS_CLASSIC_TLS); + else if (strcmp(flag, "OPENGL_LIB_NEW_TLS") == 0) + p->entries[n].flags |= (FILE_TYPE_OPENGL_LIB | + FILE_CLASS_NEW_TLS); + else if (strcmp(flag, "OPENGL_SYMLINK_CLASSIC_TLS") == 0) + p->entries[n].flags |= (FILE_TYPE_OPENGL_SYMLINK | + FILE_CLASS_CLASSIC_TLS); + else if (strcmp(flag, "OPENGL_SYMLINK_NEW_TLS") == 0) + p->entries[n].flags |= (FILE_TYPE_OPENGL_SYMLINK | + FILE_CLASS_NEW_TLS); + else if (strcmp(flag, "OPENGL_LIB_NEW_TLS_32") == 0) + p->entries[n].flags |= (FILE_TYPE_OPENGL_LIB | + FILE_CLASS_NEW_TLS_32); + else if (strcmp(flag, "OPENGL_SYMLINK_NEW_TLS_32") == 0) + p->entries[n].flags |= (FILE_TYPE_OPENGL_SYMLINK | + FILE_CLASS_NEW_TLS_32); + else + goto invalid_manifest_file; + + free(flag); + + /* libs and documentation have a path field */ + + if (p->entries[n].flags & FILE_TYPE_HAVE_PATH) { + p->entries[n].path = read_next_word(c, &c); + if (!p->entries[n].path) goto invalid_manifest_file; + } else { + p->entries[n].path = NULL; + } + + /* symlinks have a target */ + + if (p->entries[n].flags & FILE_TYPE_SYMLINK) { + p->entries[n].target = read_next_word(c, &c); + if (!p->entries[n].target) goto invalid_manifest_file; + } else { + p->entries[n].target = NULL; + } + + /* + * as a convenience for later, set the 'name' pointer to + * the basename contained in 'file' (ie the portion of + * 'file' without any leading directory components + */ + + p->entries[n].name = strrchr(p->entries[n].file, '/'); + if (p->entries[n].name) p->entries[n].name++; + + if (!p->entries[n].name) p->entries[n].name = p->entries[n].file; + + /* free the line */ + + free(buf); + } + + line++; + + } while (!done); + + if (manifest) munmap(manifest, len); + if (fd != -1) close(fd); + + return p; + + cannot_open: + ui_error(op, "Failure opening package's .manifest file (%s).", + strerror(errno)); + goto fail; + + invalid_manifest_file: + + ui_error(op, "Invalid .manifest file; error on line %d.", line); + goto fail; + + fail: + if (p && p->entries) free(p->entries); + if (p) free(p); + if (manifest) munmap(manifest, len); + if (fd != -1) close(fd); + return NULL; + +} /* parse_manifest() */ diff --git a/kernel.c b/kernel.c new file mode 100644 index 0000000..26be0ab --- /dev/null +++ b/kernel.c @@ -0,0 +1,1568 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + */ + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/utsname.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <stdlib.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/mman.h> + +#include "nvidia-installer.h" +#include "kernel.h" +#include "user-interface.h" +#include "files.h" +#include "misc.h" +#include "precompiled.h" +#include "snarf.h" +#include "crc.h" + +/* local prototypes */ + +static char *default_kernel_module_installation_path(Options *op); +static char *default_kernel_source_path(Options *op); +static int check_for_loaded_kernel_module(Options *op, const char *); +static int rmmod_kernel_module(Options *op, const char *); +static PrecompiledInfo *download_updated_kernel_interface(Options*, Package*, + const char*); +static int cc_version_check(Options *op, Package *p); +static int rivafb_check(Options *op, Package *p); +static void rivafb_module_check(Options *op, Package *p); + +static PrecompiledInfo *scan_dir(Options *op, Package *p, + const char *directory_name, + const char *output_filename, + const char *proc_version_string); + +static char *build_distro_precompiled_kernel_interface_dir(Options *op); +static char *convert_include_path_to_source_path(const char *inc); +static char *guess_kernel_module_filename(Options *op); + +/* + * Message text that is used by several error messages. + */ + +static const char install_your_kernel_source[] = +"Please make sure you have installed the kernel source files " +"for your kernel; on Red Hat Linux systems, for example, be " +"sure you have the 'kernel-source' rpm installed. If you know the " +"correct kernel source files are installed, you may specify the " +"kernel source path with the '--kernel-source-path' " +"commandline option."; + + + + +/* + * determine_kernel_module_installation_path() - get the installation + * path for the kernel module. The order is: + * + * - if op->kernel_module_installation_path is non-NULL, then it must + * have been initialized by the commandline parser, and therefore we + * should obey that (so just return). + * + * - get the default installation path + * + * - if in expert mode, ask the user, and use what they gave, if + * non-NULL + */ + +int determine_kernel_module_installation_path(Options *op) +{ + char *result; + int count = 0; + + if (op->kernel_module_installation_path) return TRUE; + + op->kernel_module_installation_path = + default_kernel_module_installation_path(op); + + if (!op->kernel_module_installation_path) return FALSE; + + if (op->expert) { + + ask_for_kernel_install_path: + + result = ui_get_input(op, op->kernel_module_installation_path, + "Kernel module installation path"); + if (result && result[0]) { + free(op->kernel_module_installation_path); + op->kernel_module_installation_path = result; + if (!confirm_path(op, op->kernel_module_installation_path)) { + return FALSE; + } + } else { + if (result) free(result); + + if (++count < NUM_TIMES_QUESTIONS_ASKED) { + ui_warn(op, "Invalid kernel module installation path."); + goto ask_for_kernel_install_path; + } else { + ui_error(op, "Unable to determine kernel module " + "installation path."); + + return FALSE; + } + } + } + + if (!mkdir_recursive(op, op->kernel_module_installation_path, 0755)) + return FALSE; + + ui_expert(op, "Kernel module installation path: %s", + op->kernel_module_installation_path); + + return TRUE; + +} /* determine_kernel_module_installation_path() */ + + + +/* + * determine_kernel_source_path() - find the qualified path to the + * kernel source tree. This is called from install_from_cwd() if we + * need to compile the kernel interface files. Assigns + * op->kernel_source_path and returns TRUE if successful. Returns + * FALSE if no kernel source tree was found. + */ + +int determine_kernel_source_path(Options *op) +{ + char *result; + int count = 0; + + /* determine the kernel source path */ + + op->kernel_source_path = default_kernel_source_path(op); + + if (op->expert) { + + ask_for_kernel_source_path: + + result = ui_get_input(op, op->kernel_source_path, + "Kernel source path"); + if (result && result[0]) { + if (!directory_exists(op, result)) { + ui_warn(op, "Kernel source path '%s' does not exist.", + result); + free(result); + + if (++count < NUM_TIMES_QUESTIONS_ASKED) { + goto ask_for_kernel_source_path; + } else { + op->kernel_source_path = NULL; + } + } else { + op->kernel_source_path = result; + } + } else { + ui_warn(op, "Invalid kernel source path."); + if (result) free(result); + + if (++count < NUM_TIMES_QUESTIONS_ASKED) { + goto ask_for_kernel_source_path; + } else { + op->kernel_source_path = NULL; + } + } + } + + /* if we STILL don't have a kernel source path, give up */ + + if (!op->kernel_source_path) { + ui_error(op, "Unable to find the kernel source tree for the " + "currently running kernel. %s", install_your_kernel_source); + + /* + * I suppose we could ask them here for the kernel source + * path, but we've already given them multiple methods of + * specifying their kernel source tree. + */ + + return FALSE; + } + + /* check that the kernel source path exists */ + + if (!directory_exists(op, op->kernel_source_path)) { + ui_error (op, "The kernel source path '%s' does not exist. %s", + op->kernel_source_path, install_your_kernel_source); + op->kernel_source_path = NULL; + return FALSE; + } + + /* check that <path>/include/linux/kernel.h exists */ + + result = nvstrcat(op->kernel_source_path, "/include/linux/kernel.h", NULL); + if (access(result, F_OK) == -1) { + ui_error(op, "The kernel header file '%s' does not exist. " + "The most likely reason for this is that the kernel source " + "path '%s' is incorrect. %s", result, + op->kernel_source_path, install_your_kernel_source); + free(result); + return FALSE; + } + free(result); + +#if 0 + + /* + * XXX The following heurisitic is broken: distribution-provided + * 2.6 kernel sources may not contain compile.h, even though they + * are valid to compile kernel modules against. One suggestion + * has been to check for autoconf.h instead. Disabling this + * entire check for now... + */ + + /* + * try to check that the kernel headers have been configured; this + * is complicated by the fact that 2.4 and 2.6 kernels have + * different header files. Here is the heuristic: + * + * if compile.h exists: + * this is a 2.6 kernel + * if $(KERNEL_SOURCES)/Makefile does not exist + * the kernel sources have not been configured + * + * if compile.h does not exit: + * this is a 2.4 kernel + * if modversions.h does not exist + * the kernel sources have not been configured + */ + + compile_h = nvstrcat(op->kernel_source_path, + "/include/linux/compile.h", NULL); + + if (access(compile_h, F_OK) == 0) { + /* compile.h exists: this is a 2.6 kernel */ + result = nvstrcat(op->kernel_source_path, "/Makefile", NULL); + } else { + /* compile.h does not exist: this is a 2.4 kernel */ + result = nvstrcat(op->kernel_source_path, + "/include/linux/modversions.h", NULL); + } + + free(compile_h); + + if (access(result, F_OK) != 0) { + ui_error(op, "The kernel header file '%s' does not exist. " + "The most likely reason for this is that the kernel " + "source files in '%s' have not been configured.", + result, op->kernel_source_path); + free(result); + return FALSE; + } + + free(result); +#endif + + /* OK, we seem to have a path to a configured kernel source tree */ + + ui_log(op, "Kernel source path: '%s'\n", op->kernel_source_path); + + return TRUE; + +} /* determine_kernel_source_path() */ + + + +/* + * link_kernel_module() - link the prebuilt kernel interface against + * the binary-only core of the kernel module. This results in a + * complete kernel module, ready for installation. + * + * + * ld -r -o nvidia.o nv-linux.o nv-kernel.o + */ + +int link_kernel_module(Options *op, Package *p) +{ + char *cmd, *result; + int ret; + + p->kernel_module_filename = guess_kernel_module_filename(op); + + cmd = nvstrcat("cd ", p->kernel_module_build_directory, + "; ", op->utils[LD], + " ", LD_OPTIONS, + " -o ", p->kernel_module_filename, + " ", PRECOMPILED_KERNEL_INTERFACE_FILENAME, + " nv-kernel.o", NULL); + + ret = run_command(op, cmd, &result, TRUE, 0, TRUE); + + free(cmd); + + if (ret != 0) { + ui_error(op, "Unable to link kernel module."); + return FALSE; + } + + ui_log(op, "Kernel module linked successfully."); + + return TRUE; + +} /* link_kernel_module() */ + + +/* + * build_kernel_module() - determine the kernel include directory, + * copy the kernel module source files into a temporary directory, and + * compile nvidia.o. + * + * XXX depends on things like make, gcc, ld, existing. Should we + * check that the user has these before doing this? + */ + +int build_kernel_module(Options *op, Package *p) +{ + char *result, *cmd, *tmp; + int len, ret; + + if (!cc_version_check(op, p)) return FALSE; + + /* + * touch all the files in the build directory to avoid make time + * skew messages + */ + + touch_directory(op, p->kernel_module_build_directory); + + + /* + * Check if conftest.sh can determine the Makefile, there's + * no hope for the make rules if this fails. + */ + cmd = nvstrcat("sh ", p->kernel_module_build_directory, + "/conftest.sh cc ", op->kernel_source_path, "/include ", + "select_makefile just_msg", NULL); + + ret = run_command(op, cmd, &result, FALSE, 0, TRUE); + nvfree(cmd); + + if (ret != 0) { + ui_error(op, result); /* display conftest.sh's error message */ + nvfree(result); + return FALSE; + } + + + if (!rivafb_check(op, p)) return FALSE; + + rivafb_module_check(op, p); + + + cmd = nvstrcat("cd ", p->kernel_module_build_directory, + "; make print-module-filename SYSSRC=", + op->kernel_source_path, NULL); + + ret = run_command(op, cmd, &p->kernel_module_filename, FALSE, 0, FALSE); + + free(cmd); + + if (ret != 0) { + ui_error(op, "Unable to determine the NVIDIA kernel module filename."); + nvfree(result); + return FALSE; + } + + ui_log(op, "Cleaning kernel module build directory."); + + len = strlen(p->kernel_module_build_directory) + 32; + cmd = nvalloc(len); + + snprintf(cmd, len, "cd %s; make clean", p->kernel_module_build_directory); + + ret = run_command(op, cmd, &result, TRUE, 0, TRUE); + free(result); + free(cmd); + + ui_status_begin(op, "Building kernel module:", "Building"); + + cmd = nvstrcat("cd ", p->kernel_module_build_directory, + "; make module SYSSRC=", op->kernel_source_path, NULL); + + ret = run_command(op, cmd, &result, TRUE, 25, TRUE); + + free(cmd); + + if (ret != 0) { + ui_status_end(op, "Error."); + ui_error(op, "Unable to build the NVIDIA kernel module."); + /* XXX need more descriptive error message */ + return FALSE; + } + + /* check that the file actually exists */ + + tmp = nvstrcat(p->kernel_module_build_directory, "/", + p->kernel_module_filename, NULL); + if (access(tmp, F_OK) == -1) { + free(tmp); + ui_status_end(op, "Error."); + ui_error(op, "The NVIDIA kernel module was not created."); + return FALSE; + } + free(tmp); + + ui_status_end(op, "done."); + + ui_log(op, "Kernel module compilation complete."); + + return TRUE; + +} /* build_kernel_module() */ + + + +/* + * build_kernel_interface() - build the kernel interface, and place it + * here: + * + * "%s/%s", p->kernel_module_build_directory, + * PRECOMPILED_KERNEL_INTERFACE_FILENAME + * + * This is done by copying the sources to a temporary working + * directory, building, and copying the kernel interface back to the + * kernel module source directory. The tmpdir is removed when + * complete. + * + * XXX this and build_kernel_module() should be merged. + */ + +int build_kernel_interface(Options *op, Package *p) +{ + char *tmpdir = NULL; + char *cmd = NULL; + char *kernel_interface = NULL; + char *dstfile = NULL; + int ret = FALSE; + int command_ret; + + /* create a temporary directory */ + + tmpdir = make_tmpdir(op); + + if (!tmpdir) { + ui_error(op, "Unable to create a temporary build directory."); + return FALSE; + } + + /* copy the kernel module sources to it */ + + ui_log(op, "Copying kernel module sources to temporary directory."); + + if (!copy_directory_contents + (op, p->kernel_module_build_directory, tmpdir)) { + ui_error(op, "Unable to copy the kernel module sources to temporary " + "directory '%s'.", tmpdir); + goto failed; + } + + /* + * touch the contents of the build directory, to avoid make time + * skew error messages + */ + + touch_directory(op, p->kernel_module_build_directory); + + /* build the kernel interface */ + + ui_status_begin(op, "Building kernel interface:", "Building"); + + cmd = nvstrcat("cd ", tmpdir, "; make ", p->kernel_interface_filename, + " SYSSRC=", op->kernel_source_path, NULL); + + command_ret = run_command(op, cmd, NULL, TRUE, 25 /* XXX */, TRUE); + + if (command_ret != 0) { + ui_status_end(op, "Error."); + ui_error(op, "Unable to build the NVIDIA kernel module interface."); + /* XXX need more descriptive error message */ + goto failed; + } + + /* check that the file exists */ + + kernel_interface = nvstrcat(tmpdir, "/", + p->kernel_interface_filename, NULL); + + if (access(kernel_interface, F_OK) == -1) { + ui_status_end(op, "Error."); + ui_error(op, "The NVIDIA kernel module interface was not created."); + goto failed; + } + + ui_status_end(op, "done."); + + ui_log(op, "Kernel module interface compilation complete."); + + /* copy the kernel interface from the tmpdir back to the srcdir */ + + dstfile = nvstrcat(p->kernel_module_build_directory, "/", + PRECOMPILED_KERNEL_INTERFACE_FILENAME, NULL); + + if (!copy_file(op, kernel_interface, dstfile, 0644)) goto failed; + + ret = TRUE; + + failed: + + remove_directory(op, tmpdir); + + if (tmpdir) nvfree(tmpdir); + if (cmd) nvfree(cmd); + if (kernel_interface) nvfree(kernel_interface); + if (dstfile) nvfree(dstfile); + + return ret; + +} /* build_kernel_interface() */ + + + +/* + * test_kernel_module() - attempt to insmod the kernel module and then + * rmmod it. Return TRUE if the insmod succeeded, or FALSE otherwise. + * + * Pass the special silence_nvidia_output option to the kernel module + * to prevent any console output while testing. + */ + +int test_kernel_module(Options *op, Package *p) +{ + char *cmd = NULL, *data; + int ret; + + /* + * If we're building/installing for a different kernel, then we + * can't test the module now. + */ + + if (op->kernel_name) return TRUE; + + cmd = nvstrcat(op->utils[INSMOD], " ", + p->kernel_module_build_directory, "/", + p->kernel_module_filename, " silence_nvidia_output=1", + NULL); + + /* only output the result of the test if in expert mode */ + + ret = run_command(op, cmd, &data, op->expert, 0, TRUE); + + if (ret != 0) { + ui_error(op, "Unable to load the kernel module '%s'. This is " + "most likely because the kernel module was built using " + "the wrong kernel source files. %s", + p->kernel_module_filename, install_your_kernel_source); + + /* + * if in expert mode, run_command() would have caused this to + * be written to the log file; so if not in expert mode, print + * the output now. + */ + + if (!op->expert) ui_log(op, "Kernel module load error: %s", data); + ret = FALSE; + } else { + free(cmd); + cmd = nvstrcat(op->utils[RMMOD], " ", p->kernel_module_name, NULL); + run_command(op, cmd, NULL, FALSE, 0, TRUE); + /* what if we fail to rmmod? */ + ret = TRUE; + } + + if (cmd) free(cmd); + if (data) free(data); + + return ret; + +} /* test_kernel_module() */ + + + +/* + * load_kernel_module() - modprobe the kernel module + */ + +int load_kernel_module(Options *op, Package *p) +{ + char *cmd, *data; + int len, ret; + + len = strlen(op->utils[MODPROBE]) + strlen(p->kernel_module_name) + 2; + + cmd = (char *) nvalloc(len); + + snprintf(cmd, len, "%s %s", op->utils[MODPROBE], p->kernel_module_name); + + ret = run_command(op, cmd, &data, FALSE, 0, TRUE); + + if (ret != 0) { + if (op->expert) { + ui_error(op, "Unable to load the kernel module: '%s'", data); + } else { + ui_error(op, "Unable to load the kernel module."); + } + ret = FALSE; + } else { + ret = TRUE; + } + + if (cmd) free(cmd); + if (data) free(data); + + return ret; + +} /* load_kernel_module() */ + + + + + + +/* + * check_kernel_module_version() - check that the driver version + * indicated in the /proc filesystem is the same as the driver version + * specified in the package description. + */ + +int check_kernel_module_version(Options *op, Package *p) +{ + int major, minor, patch; + int proc_major, proc_minor, proc_patch; + FILE *fp = 0; + char *buf; + int eof; + + fp = fopen(NVIDIA_VERSION_PROC_FILE, "r"); + buf = fget_next_line(fp, &eof); + + if (!nvid_version(buf, &proc_major, &proc_minor, &proc_patch)) { + free(buf); + return FALSE; + } + + if (!nvid_version(p->version_string, &major, &minor, &patch)) { + return FALSE; + } + + if ((proc_major != major) || + (proc_minor != minor) || + (proc_patch != patch)) { + return FALSE; + } + + return TRUE; + +} /* check_kernel_module_version() */ + + + +/* + * check_for_unloaded_kernel_module() - test if any of the "bad" + * kernel modules are loaded; if they are, then try to unload it. If + * we can't unload it, then report an error and return FALSE; + */ + +int check_for_unloaded_kernel_module(Options *op, Package *p) +{ + int n = 0; + int loaded = FALSE; + unsigned int bits = 0; + + /* + * We can skip this check if we are installing for a non-running + * kernel and only installing a kernel module. + */ + + if (op->kernel_module_only && op->kernel_name) { + ui_log(op, "Only installing a kernel module for a non-running " + "kernel; skipping the \"is an NVIDIA kernel module loaded?\" " + "test."); + return TRUE; + } + + while (p->bad_modules[n]) { + if (check_for_loaded_kernel_module(op, p->bad_modules[n])) { + loaded = TRUE; + bits |= (1 << n); + } + n++; + } + + if (!loaded) return TRUE; + + /* one or more kernel modules is loaded... try to unload them */ + + n = 0; + while (p->bad_modules[n]) { + if (!(bits & (1 << n))) { + n++; + continue; + } + + rmmod_kernel_module(op, p->bad_modules[n]); + + /* check again */ + + if (check_for_loaded_kernel_module(op, p->bad_modules[n])) { + ui_error(op, "An NVIDIA kernel module '%s' appears to already " + "be loaded in your kernel. This may be because it is " + "in use (for example, by the X server). Please be " + "sure you have exited X before attempting to upgrade " + "your driver. If you have exited X but still receive " + "this message, then an error has occured that has " + "confused the usage count of the kernel module; the " + "simplest remedy is to reboot your computer.", + p->bad_modules[n]); + + return FALSE; + } + n++; + } + + return TRUE; + +} /* check_for_unloaded_kernel_module() */ + + + + + +/* + * find_precompiled_kernel_interface() - do assorted black magic to + * determine if the given package contains a precompiled kernel interface + * for the kernel on this system. + * + * XXX it would be nice to extend this so that a kernel module could + * be installed for a kernel other than the currently running one. + */ + +int find_precompiled_kernel_interface(Options *op, Package *p) +{ + char *proc_version_string, *output_filename, *tmp; + PrecompiledInfo *info = NULL; + + /* allow the user to completely skip this search */ + + if (op->no_precompiled_interface) { + ui_log(op, "Not probing for precompiled kernel interfaces."); + return FALSE; + } + + /* retrieve the proc version string for the running kernel */ + + proc_version_string = read_proc_version(op); + + if (!proc_version_string) goto failed; + + /* make sure the target directory exists */ + + if (!mkdir_recursive(op, p->kernel_module_build_directory, 0755)) + goto failed; + + /* build the output filename */ + + output_filename = nvstrcat(p->kernel_module_build_directory, "/", + PRECOMPILED_KERNEL_INTERFACE_FILENAME, NULL); + + /* + * if the --precompiled-kernel-interfaces-path option was + * specified, search that directory, first + */ + + if (op->precompiled_kernel_interfaces_path) { + info = scan_dir(op, p, op->precompiled_kernel_interfaces_path, + output_filename, proc_version_string); + } + + /* + * If we didn't find a match, search for distro-provided + * precompiled kernel interfaces + */ + + if (!info) { + tmp = build_distro_precompiled_kernel_interface_dir(op); + if (tmp) { + info = scan_dir(op, p, tmp, output_filename, proc_version_string); + nvfree(tmp); + } + } + + /* + * if we still haven't found a match, search in + * p->precompiled_kernel_interface_directory (the directory + * containing the precompiled kernel interfaces shipped with the + * package) + */ + + if (!info) { + info = scan_dir(op, p, p->precompiled_kernel_interface_directory, + output_filename, proc_version_string); + } + + /* + * If we didn't find a matching precompiled kernel interface, ask + * if we should try to download one. + */ + + if (!info && !op->no_network) { + if (ui_yes_no(op, TRUE, "No precompiled kernel interface was " + "found to match " + "your kernel; would you like the installer to attempt " + "to download a kernel interface for your kernel from " + "the NVIDIA ftp site (%s)?", op->ftp_site)) { + + info = download_updated_kernel_interface(op, p, + proc_version_string); + if (!info) { + ui_message(op, "No matching precompiled kernel interface was " + "found on the NVIDIA ftp site; this means that the " + "installer will need to compile a kernel interface " + "for your kernel."); + return FALSE; + } + } + } + + /* If we found one, ask expert users if they really want to use it */ + + if (info && op->expert) { + if (!ui_yes_no(op, TRUE, "A precompiled kernel interface for the " + "kernel '%s' has been found. Would you like to " + "use this? (answering 'no' will require the " + "installer to compile the interface)", + info->description)) { + /* XXX free info */ + info = NULL; + } + } + + if (info) { + /* XXX free info */ + return TRUE; + } + + failed: + + ui_message(op, "No precompiled kernel interface was found to match " + "your kernel; this means that the installer will need to " + "compile a new kernel interface."); + + return FALSE; + +} /* find_precompiled_kernel_interface() */ + + + +/* + * get_kernel_name() - get the kernel name: this is either what + * the user specified via the --kernel-name option, or `name -r`. + */ + +char __kernel_name[256]; + +char *get_kernel_name(Options *op) +{ + struct utsname uname_buf; + + if (op->kernel_name) { + return op->kernel_name; + } else { + if (uname(&uname_buf) == -1) { + ui_warn(op, "Unable to determine kernel version (%s).", + strerror(errno)); + return NULL; + } else { + strncpy(__kernel_name, uname_buf.release, 256); + return __kernel_name; + } + } +} /* get_kernel_name() */ + + + +/* + *************************************************************************** + * local static routines + *************************************************************************** + */ + + + +/* + * default_kernel_module_installation_path() - do the equivalent of: + * + * SYSSRC = /lib/modules/$(shell uname -r) + * + * ifeq ($(shell if test -d $(SYSSRC)/kernel; then echo yes; fi),yes) + * INSTALLDIR = $(SYSSRC)/kernel/drivers/video + * else + * INSTALLDIR = $(SYSSRC)/video + * endif + */ + +static char *default_kernel_module_installation_path(Options *op) +{ + char *str, *tmp; + + tmp = get_kernel_name(op); + if (!tmp) return NULL; + + str = nvstrcat("/lib/modules/", tmp, "/kernel", NULL); + + if (directory_exists(op, str)) { + free(str); + str = nvstrcat("/lib/modules/", tmp, "/kernel/drivers/video", NULL); + return str; + } + + free(str); + + str = nvstrcat("/lib/modules/", tmp, "/video", NULL); + + return str; + +} /* default_kernel_module_installation_path() */ + + + +/* + * default_kernel_source_path() - determine the default kernel + * source path, if possible. Return NULL if no default kernel path + * is found. + * + * Here is the logic: + * + * if --kernel-source-path was set, use that + * + * if --kernel-include-path was set, use that (converting it to the + * source path); also print a warning that --kernel-include-path is + * deprecated. + * + * else if SYSSRC is set, use that + * + * else if /lib/modules/`uname -r`/build exists use that + * + * else if /usr/src/linux exists use that + * + * else return NULL + * + * One thing to note is that for the first two methods + * (--kernel-source-path and $SYSSRC) we don't check for directory + * existence before returning. This is intentional: if the user set + * one of these, then they're trying to set a particular path. If + * that directory doesn't exist, then better to abort installation with + * an appropriate error message in determine_kernel_source_path(). + * Whereas, for the later two (/lib/modules/`uname -r`/build + * and /usr/src/linux), these are not explicitly requested by + * the user, so it makes sense to only use them if they exist. + */ + +static char *default_kernel_source_path(Options *op) +{ + char *str, *tmp; + + str = tmp = NULL; + + /* check --kernel-source-path */ + + if (op->kernel_source_path) { + ui_log(op, "Using the kernel source path '%s' as specified by the " + "'--kernel-source-path' commandline option.", + op->kernel_source_path); + return op->kernel_source_path; + } + + /* check --kernel-include-path */ + + if (op->kernel_include_path) { + ui_warn(op, "The \"--kernel-include-path\" option is deprecated " + "(as part of reorganization to support Linux 2.6); please use " + "\"--kernel-source-path\" instead."); + str = convert_include_path_to_source_path(op->kernel_include_path); + ui_log(op, "Using the kernel source path '%s' (inferred from the " + "'--kernel-include-path' commandline option '%s').", + str, op->kernel_include_path); + return str; + } + + /* check SYSSRC */ + + str = getenv("SYSSRC"); + if (str) { + ui_log(op, "Using the kernel source path '%s', as specified by the " + "SYSSRC environment variable.", str); + return str; + } + + /* check /lib/modules/`uname -r`/build and /usr/src/linux-`uname -r` */ + + tmp = get_kernel_name(op); + + if (tmp) { + str = nvstrcat("/lib/modules/", tmp, "/build", NULL); + + if (directory_exists(op, str)) { + return str; + } + + nvfree(str); + + /* + * check "/usr/src/linux-`uname -r`", too; patch suggested by + * Peter Berg Larsen <pebl@math.ku.dk> + */ + + str = nvstrcat("/usr/src/linux-", tmp, NULL); + if (directory_exists(op, str)) { + return str; + } + + free(str); + } + + /* finally, try /usr/src/linux */ + + if (directory_exists(op, "/usr/src/linux")) { + return "/usr/src/linux"; + } + + return NULL; + +} /* default_kernel_source_path() */ + + +/* + * check_for_loaded_kernel_module() - check if the specified kernel + * module is currently loaded using `lsmod`. Returns TRUE if the + * kernel module is loaded; FALSE if it is not. + * + * Be sure to check that the character following the kernel module + * name is a space (to avoid getting false positivies when the given + * kernel module name is contained within another kernel module name. + */ + +static int check_for_loaded_kernel_module(Options *op, const char *module_name) +{ + char *ptr, *result = NULL; + int ret; + + ret = run_command(op, op->utils[LSMOD], &result, FALSE, 0, TRUE); + + if ((ret == 0) && (result) && (result[0] != '\0')) { + ptr = strstr(result, module_name); + if (ptr) { + ptr += strlen(module_name); + if(!isspace(*ptr)) ret = 1; + } else { + ret = 1; + } + } + + if (result) free(result); + + return ret ? FALSE : TRUE; + +} /* check_for_loaded_kernel_module() */ + + +/* + * rmmod_kernel_module() - run `rmmod nvidia` + */ + +static int rmmod_kernel_module(Options *op, const char *module_name) +{ + int len, ret; + char *cmd; + + len = strlen(op->utils[RMMOD]) + strlen(module_name) + 2; + + cmd = (char *) nvalloc(len); + + snprintf(cmd, len, "%s %s", op->utils[RMMOD], module_name); + + ret = run_command(op, cmd, NULL, FALSE, 0, TRUE); + + free(cmd); + + return ret ? FALSE : TRUE; + +} /* rmmod_kernel_module() */ + + + +/* + * get_updated_kernel_interfaces() - + */ + +static PrecompiledInfo * +download_updated_kernel_interface(Options *op, Package *p, + const char *proc_version_string) +{ + int fd = -1; + int dst_fd = -1; + char *url = NULL; + char *tmpfile = NULL; + char *dstfile = NULL; + char *buf = NULL; + char *output_filename = NULL; + char *str = (void *) -1; + char *ptr, *s; + struct stat stat_buf; + PrecompiledInfo *info = NULL; + uint32 crc; + + /* initialize the tmpfile and url strings */ + + tmpfile = nvstrcat(op->tmpdir, "/nv-updates-XXXXXX", NULL); + url = nvstrcat(op->ftp_site, "/XFree86/", INSTALLER_OS, "-", + INSTALLER_ARCH, "/", p->version_string, + "/updates/updates.txt", NULL); + + /* + * create a temporary file in which to write the list of available + * updates + */ + + if ((fd = mkstemp(tmpfile)) == -1) { + ui_error(op, "Unable to create temporary file (%s)", strerror(errno)); + goto done; + } + + /* download the updates list */ + + if (!snarf(op, url, fd, SNARF_FLAGS_DOWNLOAD_SILENT)) goto done; + + /* get the length of the file */ + + if (fstat(fd, &stat_buf) == -1) goto done; + + /* map the file into memory for easier reading */ + + str = mmap(0, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0); + if (str == (void *) -1) goto done; + + /* + * loop over each line of the updates file: each line should be of + * the format: "[filename]:::[proc version string]" + */ + + ptr = str; + + while (TRUE) { + buf = get_next_line(ptr, &ptr); + if ((!buf) || (buf[0] == '\0')) goto done; + + s = strstr(buf, ":::"); + if (!s) { + ui_error(op, "Invalid updates.txt list."); + goto done; + } + + s += 3; /* skip past the ":::" separator */ + + if (strcmp(proc_version_string, s) == 0) { + + /* proc versions strings match */ + + /* + * terminate the string at the start of the ":::" + * separator so that buf is the filename + */ + + s -= 3; + *s = '\0'; + + /* build the new url and dstfile strings */ + + nvfree(url); + url = nvstrcat(op->ftp_site, "/XFree86/", + INSTALLER_OS, "-", INSTALLER_ARCH, "/", + p->version_string, "/updates/", buf, NULL); + + dstfile = nvstrcat(p->precompiled_kernel_interface_directory, + "/", buf, NULL); + + /* create dstfile */ + + dst_fd = creat(dstfile, S_IRUSR | S_IWUSR); + if (dst_fd == -1) { + ui_error(op, "Unable to create file '%s' (%s).", + dstfile, strerror(errno)); + goto done; + } + + /* download the file */ + + if (!snarf(op, url, dst_fd, SNARF_FLAGS_STATUS_BAR)) goto done; + + close(dst_fd); + dst_fd = -1; + + /* XXX once we have gpg setup, should check the file here */ + + /* build the output filename string */ + + output_filename = nvstrcat(p->kernel_module_build_directory, "/", + PRECOMPILED_KERNEL_INTERFACE_FILENAME, + NULL); + + /* unpack the downloaded file */ + + info = precompiled_unpack(op, dstfile, output_filename, + proc_version_string, + p->major, p->minor, p->patch); + + /* compare checksums */ + + crc = compute_crc(op, output_filename); + + if (info && (info->crc != crc)) { + ui_error(op, "The embedded checksum of the downloaded file " + "'%s' (%d) does not match the computed checksum ", + "(%d); not using.", buf, info->crc, crc); + unlink(dstfile); + /* XXX free info */ + info = NULL; + } + + goto done; + } + + nvfree(buf); + } + + + done: + + if (dstfile) nvfree(dstfile); + if (buf) nvfree(buf); + if (str != (void *) -1) munmap(str, stat_buf.st_size); + if (dst_fd > 0) close(dst_fd); + if (fd > 0) close(fd); + + unlink(tmpfile); + if (tmpfile) nvfree(tmpfile); + if (url) nvfree(url); + + return info; + +} /* get_updated_kernel_interfaces() */ + + + +/* + * cc_version_check() - + */ + +static int cc_version_check(Options *op, Package *p) +{ + char *cmd, *CC, *result; + int ret; + + /* + * If we're building/installing for a different kernel, then we + * can't do the gcc version check (we don't have a /proc/version + * string from which to get the kernel's gcc version). + */ + + if (op->kernel_name) { + setenv("IGNORE_CC_MISMATCH", "1", 1); + return TRUE; + } + + + CC = getenv("CC"); + if (!CC) CC = "cc"; + + ui_log(op, "Performing cc_version_check with CC=\"%s\".", CC); + + cmd = nvstrcat("sh ", p->kernel_module_build_directory, + "/conftest.sh ", CC, " ", op->kernel_source_path, + "/include ", "cc_sanity_check just_msg", NULL); + + ret = run_command(op, cmd, &result, FALSE, 0, TRUE); + + nvfree(cmd); + + if (ret == 0) return TRUE; + + ret = ui_yes_no(op, TRUE, "gcc-version-check failed:\n\n%s\n\n" + "If you know what you are doing and want to " + "ignore the gcc version check, select \"No\" to " + "continue installation. Otherwise, select \"Yes\" to " + "abort installation, set the CC environment variable to " + "the name of the compiler used to compile your kernel, " + "and restart installation. Abort now?", result); + + nvfree(result); + + if (!ret) setenv("IGNORE_CC_MISMATCH", "1", 1); + + return !ret; + +} /* cc_version_check() */ + + + +/* + * rivafb_check() - run the rivafb_sanity_check conftest; if the test + * fails, print the error message from the test and abort driver + * installation. + */ + +static int rivafb_check(Options *op, Package *p) +{ + char *cmd, *result; + int ret; + + ui_log(op, "Performing rivafb check."); + + cmd = nvstrcat("sh ", p->kernel_module_build_directory, + "/conftest.sh cc ", op->kernel_source_path, "/include ", + "rivafb_sanity_check just_msg", NULL); + + ret = run_command(op, cmd, &result, FALSE, 0, TRUE); + + nvfree(cmd); + + if (ret == 0) return TRUE; + + ui_error(op, result); + + nvfree(result); + + return FALSE; + +} /* rivafb_check() */ + + + +/* + * rivafb_module_check() - run the rivafb_module_sanity_check + * conftest; if the test prints anything, print the warning text + * outputted by the test. + */ + +static void rivafb_module_check(Options *op, Package *p) +{ + char *cmd, *result; + int ret; + + ui_log(op, "Performing rivafb module check."); + + cmd = nvstrcat("sh ", p->kernel_module_build_directory, + "/conftest.sh cc ", op->kernel_source_path, "/include ", + "rivafb_module_sanity_check just_msg", NULL); + + ret = run_command(op, cmd, &result, FALSE, 0, TRUE); + + nvfree(cmd); + + if (result && result[0]) { + ui_warn(op, result); + } + + nvfree(result); + +} /* rivafb_module_check() */ + + + +/* + * scan_dir() - scan through the specified directory for a matching + * precompiled kernel interface. + */ + +static PrecompiledInfo *scan_dir(Options *op, Package *p, + const char *directory_name, + const char *output_filename, + const char *proc_version_string) +{ + DIR *dir; + struct dirent *ent; + PrecompiledInfo *info = NULL; + char *filename; + + if (!directory_name) return NULL; + + dir = opendir(directory_name); + if (!dir) return NULL; + + /* + * loop over all contents of the directory, looking for a + * precompiled kernel interface that matches the running kernel + */ + + while ((ent = readdir(dir)) != NULL) { + + if (((strcmp(ent->d_name, ".")) == 0) || + ((strcmp(ent->d_name, "..")) == 0)) continue; + + filename = nvstrcat(directory_name, "/", ent->d_name, NULL); + + info = precompiled_unpack(op, filename, output_filename, + proc_version_string, + p->major, p->minor, p->patch); + + if (info) break; + + free(filename); + filename = NULL; + } + + if (closedir(dir) != 0) { + ui_error(op, "Failure while closing directory '%s' (%s).", + directory_name, + strerror(errno)); + } + + return info; + +} /* scan_dir() */ + + + +/* + * build_distro_precompiled_kernel_interface_dir() - construct this + * path: + * + * /lib/modules/precompiled/`uname -r`/nvidia/gfx/ + */ + +static char *build_distro_precompiled_kernel_interface_dir(Options *op) +{ + struct utsname uname_buf; + char *str; + + if (uname(&uname_buf) == -1) { + ui_error(op, "Unable to determine kernel version (%s)", + strerror(errno)); + return NULL; + } + + str = nvstrcat("/lib/modules/precompiled/", uname_buf.release, + "/nvidia/gfx/", NULL); + + return str; + +} /* build_distro_precompiled_kernel_interface_dir() */ + + + +/* + * convert_include_path_to_source_path() - given input to + * "--kernel-include-path", convert it to "--kernel-source-path" by + * scanning from the end to the previous "/". + */ + +static char *convert_include_path_to_source_path(const char *inc) +{ + char *c, *str; + + str = nvstrdup(inc); + + /* go to the end of the string */ + + for (c = str; *c; c++); + + /* move to the last printable character */ + + c--; + + /* if the string ends in '/'; backup one more */ + + if (*c == '/') c--; + + /* now back up to the next '/' */ + + while ((c >= str) && (*c != '/')) c--; + + if (*c == '/') *c = '\0'; + + return str; + +} /* convert_include_path_to_source_path() */ + + + +/* + * guess_kernel_module_filename() - parse uname to decide if the + * kernel module filename is "nvidia.o" or "nvidia.ko". + */ + +static char *guess_kernel_module_filename(Options *op) +{ + struct utsname uname_buf; + char *tmp, *str, *dot0, *dot1; + int major, minor; + + if (op->kernel_name) { + str = op->kernel_name; + } else { + if (uname(&uname_buf) == -1) { + ui_error (op, "Unable to determine kernel version (%s)", + strerror (errno)); + return NULL; + } + str = uname_buf.release; + } + + tmp = nvstrdup(str); + + dot0 = strchr(tmp, '.'); + if (!dot0) goto fail; + + *dot0 = '\0'; + + major = atoi(tmp); + + dot0++; + dot1 = strchr(dot0, '.'); + if (!dot1) goto fail; + + *dot1 = '\0'; + + minor = atoi(dot0); + + if ((major > 2) || ((major == 2) && (minor > 4))) { + return "nvidia.ko"; + } else { + return "nvidia.o"; + } + + fail: + ui_error (op, "Unable to determine if kernel is 2.6.0 or greater from " + "uname string '%s'; assuming the kernel module filename is " + "'nvidia.o'.", str); + return "nvidia.o"; + +} /* guess_kernel_module_filename() */ diff --git a/kernel.h b/kernel.h new file mode 100644 index 0000000..75dcc8f --- /dev/null +++ b/kernel.h @@ -0,0 +1,43 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + */ + +#ifndef __NVIDIA_INSTALLER_KERNEL_H__ +#define __NVIDIA_INSTALLER_KERNEL_H__ + +#include "nvidia-installer.h" + +int determine_kernel_module_installation_path (Options*); +int determine_kernel_source_path (Options*); +int link_kernel_module (Options*, Package*); +int build_kernel_module (Options*, Package*); +int build_kernel_interface (Options*, Package*); +int test_kernel_module (Options*, Package*); +int load_kernel_module (Options*, Package*); +int check_kernel_module_version (Options*, Package*); +int check_for_unloaded_kernel_module (Options*, Package*); +int find_precompiled_kernel_interface (Options*, Package *); +char *get_kernel_name (Options*); + + +#endif /* __NVIDIA_INSTALLER_KERNEL_H__ */ @@ -0,0 +1,168 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * log.c + */ + +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <time.h> + +#include "nvidia-installer.h" +#include "misc.h" +#include "format.h" + +/* global stream for log output */ + +static FILE *log_file_stream; + + +/* convenience macro for logging boolean values */ + +#define BOOLSTR(b) ((b) ? "true" : "false") + +/* convenience macro for catching NULL strings */ + +#define STRSTR(x) ((x) ? (x) : "(not specified)") + + +/* + * log_init() - if logging is enabled, initialize the log file; if + * initializing the log file fails, print an error to stderr and + * disable loggging. If initialization succeeds, write a header line + * and the state of all noteworthy options. + */ + +void log_init(Options *op) +{ + time_t now; + + if (!op->logging) return; + + log_file_stream = fopen(op->log_file_name, "w"); + + if (!log_file_stream) { + fprintf(stderr, "%s: Error opening log file '%s' for " + "writing (%s); disabling logging.\n", + PROGRAM_NAME, op->log_file_name, strerror(errno)); + op->logging = FALSE; + return; + } + + log_printf(op, TRUE, NULL, "%s log file '%s'", + PROGRAM_NAME, op->log_file_name); + + now = time(NULL); + log_printf(op, TRUE, NULL, "creation time: %s", ctime(&now)); + + log_printf(op, TRUE, NULL, ""); + + log_printf(op, TRUE, NULL, "option status:"); + log_printf(op, TRUE, NULL, " license pre-accepted : %s", + BOOLSTR(op->accept_license)); + log_printf(op, TRUE, NULL, " update : %s", + BOOLSTR(op->update)); + log_printf(op, TRUE, NULL, " force update : %s", + BOOLSTR(op->force_update)); + log_printf(op, TRUE, NULL, " expert : %s", + BOOLSTR(op->expert)); + log_printf(op, TRUE, NULL, " uninstall : %s", + BOOLSTR(op->uninstall)); + log_printf(op, TRUE, NULL, " driver info : %s", + BOOLSTR(op->driver_info)); + log_printf(op, TRUE, NULL, " no precompiled interface: %s", + BOOLSTR(op->no_precompiled_interface)); + log_printf(op, TRUE, NULL, " no ncurses color : %s", + BOOLSTR(op->no_ncurses_color)); + log_printf(op, TRUE, NULL, " query latest driver ver : %s", + BOOLSTR(op->latest)); + log_printf(op, TRUE, NULL, " OpenGL header files : %s", + BOOLSTR(op->opengl_headers)); + log_printf(op, TRUE, NULL, " no questions : %s", + BOOLSTR(op->no_questions)); + log_printf(op, TRUE, NULL, " silent : %s", + BOOLSTR(op->silent)); + log_printf(op, TRUE, NULL, " X install prefix : %s", + STRSTR(op->xfree86_prefix)); + log_printf(op, TRUE, NULL, " OpenGL install prefix : %s", + STRSTR(op->opengl_prefix)); + log_printf(op, TRUE, NULL, " Installer install prefix: %s", + STRSTR(op->installer_prefix)); + log_printf(op, TRUE, NULL, " kernel source path : %s", + STRSTR(op->kernel_source_path)); + log_printf(op, TRUE, NULL, " kernel install path : %s", + STRSTR(op->kernel_module_installation_path)); + log_printf(op, TRUE, NULL, " proc mount point : %s", + STRSTR(op->proc_mount_point)); + log_printf(op, TRUE, NULL, " ui : %s", + STRSTR(op->ui_str)); + log_printf(op, TRUE, NULL, " tmpdir : %s", + STRSTR(op->tmpdir)); + log_printf(op, TRUE, NULL, " ftp site : %s", + STRSTR(op->ftp_site)); + + + log_printf(op, TRUE, NULL, ""); + +} /* log_init() */ + + + +/* + * log_printf() - if the logggin option is set, this function writes + * the given printf-style input to the log_file_stream; if the logging + * option is not set, then nothing is done here. + */ + +#define LOG_WIDTH 79 + +void log_printf(Options *op, const int wb, + const char *prefix, const char *fmt, ...) +{ + char *buf; + int i; + TextRows *t; + va_list ap; + + if (!op->logging) return; + + va_start(ap, fmt); + + buf = assemble_string(fmt, ap); + t = nv_format_text_rows(prefix, buf, LOG_WIDTH, wb); + + for (i = 0; i < t->n; i++) fprintf(log_file_stream, "%s\n", t->t[i]); + + nv_free_text_rows(t); + nvfree(buf); + + va_end(ap); + + /* flush, just to be safe */ + + fflush(log_file_stream); + +} /* log_printf() */ @@ -0,0 +1,1405 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * misc.c - this source file contains miscellaneous routines for use + * by the nvidia-installer. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/stat.h> +#include <ctype.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <dirent.h> +#include <libgen.h> + +#include "nvidia-installer.h" +#include "user-interface.h" +#include "kernel.h" +#include "files.h" +#include "misc.h" +#include "crc.h" + +static int check_symlink(Options*, const char*, const char*, const char*); +static int check_file(Options*, const char*, const mode_t, const uint32); +static char *find_system_util(char *util); + + +/* + * nvalloc() - malloc wrapper that checks for errors, and zeros out + * the memory; if an error occurs, an error is printed to stderr and + * exit is called -- this function will only return on success. + */ + +void *nvalloc(size_t size) +{ + void *m = malloc(size); + + if (!m) { + fprintf(stderr, "%s: memory allocation failure (%s)! \n", + PROGRAM_NAME, strerror(errno)); + exit(1); + } + memset((char *) m, 0, size); + return m; + +} /* nvalloc() */ + + + +/* + * nvrealloc() - realloc wrapper that checks for errors; if an error + * occurs, an error is printed to stderr and exit is called -- this + * function will only return on success. + */ + +void *nvrealloc(void *ptr, size_t size) +{ + void *m; + + if (ptr == NULL) return nvalloc(size); + + m = realloc(ptr, size); + if (!m) { + fprintf(stderr, "%s: memory re-allocation failure (%s)! \n", + PROGRAM_NAME, strerror(errno)); + exit(1); + } + return m; + +} /* nvrealloc() */ + + + +/* + * nvstrdup() - wrapper for strdup() that checks the return value; if + * an error occurs, an error is printed to stderr and exit is called + * -- this function will only return on success. + */ + +char *nvstrdup(const char *s) +{ + char *m; + + if (!s) return NULL; + + m = strdup(s); + + if (!m) { + fprintf(stderr, "%s: memory allocation failure during strdup (%s)! \n", + PROGRAM_NAME, strerror(errno)); + exit(1); + } + return m; + +} /* nvstrdup() */ + + + +/* + * nvfree() - + */ +void nvfree(char *s) +{ + if (s) free(s); + +} /* nvfree() */ + + + +/* + * nvstrtolower() - convert the given string to lowercase. + */ + +char *nvstrtolower(char *s) +{ + char *start = s; + + if (s == NULL) return NULL; + + while (*s) { + *s = tolower(*s); + s++; + } + + return start; + +} /* nvstrtolower() */ + + + +/* + * nvstrcat() - allocate a new string, copying all given strings + * into it. taken from glib + */ + +char *nvstrcat(const char *str, ...) +{ + unsigned int l; + va_list args; + char *s; + char *concat; + + l = 1 + strlen(str); + va_start(args, str); + s = va_arg(args, char *); + + while (s) { + l += strlen(s); + s = va_arg(args, char *); + } + va_end(args); + + concat = nvalloc(l); + concat[0] = 0; + + strcat(concat, str); + va_start(args, str); + s = va_arg(args, char *); + while (s) { + strcat(concat, s); + s = va_arg(args, char *); + } + va_end(args); + + return concat; + +} /* nvstrcat() */ + + + +/* + * read_next_word() - given a string buf, skip any whitespace, and + * then copy the next set of characters until more white space is + * encountered. A new string containing this next word is returned. + * The passed-by-reference parameter e, if not NULL, is set to point + * at the where the end of the word was, to facilitate multiple calls + * of read_next_word(). + */ + +char *read_next_word (char *buf, char **e) +{ + char *c = buf; + char *start, *ret; + int len; + + while ((*c) && (isspace (*c)) && (*c != '\n')) c++; + start = c; + while ((*c) && (!isspace (*c)) && (*c != '\n')) c++; + + len = c - start; + + if (len == 0) return NULL; + + ret = (char *) nvalloc (len + 1); + + strncpy (ret, start, len); + ret[len] = '\0'; + + if (e) *e = c; + + return ret; + +} /* read_next_word() */ + + + +/* + * assemble_string() - takes a fmt string and a va_list, and uses + * vsnprintf to build the resulting string. The caller of this + * function is responsible for freeing the returned string. + * + * XXX replace this with a macro (see X driver sources) + */ + +char *assemble_string(const char *fmt, va_list ap) +{ + char *buf; + int current_len, len, done; + + if (!fmt) return NULL; + + done = FALSE; + current_len = NV_LINE_LEN; + + do { + buf = (char *) nvalloc (current_len); + + len = vsnprintf (buf, current_len, fmt, ap); + + if ((len == -1) || len > current_len) { + + /* + * if we get in here we know that vsnprintf had to + * truncate the string to make it fit in the buffer... we + * need to extend the buffer to encompass the string. + * Unfortunately, we have to deal with two different + * semantics of the return value from (v)snprintf: + * + * -1 when the buffer is not long enough (glibc < 2.1) + * + * or + * + * the length the string would have been if the buffer had + * been large enough (glibc >= 2.1) + */ + + if (len == -1) current_len += NV_LINE_LEN; + else current_len = len+1; + + free (buf); + } + else done = TRUE; + + } while (!done); + + return buf; + +} /* assemble_string() */ + + + + +/* + * check_euid() - this function checks that the effective uid of this + * application is root, and calls the ui to print an error if it's not + * root. + */ + +int check_euid(Options *op) +{ + uid_t euid; + + euid = geteuid(); + + if (euid != 0) { + ui_error(op, "nvidia-installer must be run as root"); + return FALSE; + } + + return TRUE; + +} /* check_euid() */ + + + +/* + * check_runlevel() - attempt to run the `runlevel` program. If we + * are in runlevel 1, explain why that is bad, and ask the user if + * they want to continue anyway. + */ + +int check_runlevel(Options *op) +{ + int ret; + char *data, *cmd; + char ignore, runlevel; + + if (op->no_runlevel_check) return TRUE; + + cmd = find_system_util("runlevel"); + if (!cmd) { + ui_warn(op, "Skipping the runlevel check (the utility " + "`runlevel` was not found)."); + return TRUE; + } + + ret = run_command(op, cmd, &data, FALSE, FALSE, TRUE); + nvfree(cmd); + + if ((ret != 0) || (!data)) { + ui_warn(op, "Skipping the runlevel check (the utility " + "`runlevel` failed to run)."); + return TRUE; + } + + ret = sscanf(data, "%c %c", &ignore, &runlevel); + + if (ret != 2) { + ui_warn(op, "Skipping the runlevel check (unrecognized output from " + "the `runlevel` utility: '%d').", data); + nvfree(data); + return TRUE; + } + + nvfree(data); + + if (runlevel == 's' || runlevel == 'S' || runlevel == '1') { + ret = ui_yes_no(op, TRUE, "You appear to be running in runlevel 1; " + "this may cause problems. For example: some " + "distributions that use devfs do not run the devfs " + "daemon in runlevel 1, making it difficult for " + "nvidia-installer to correctly setup the kernel " + "module configuration files. It is recommended " + "that you quit installation now and switch to " + "runlevel 3 (`telinit 3`) before installing.\n\n" + "Quit installation now? (select 'No' to continue " + "installation)"); + + if (ret) return FALSE; + } + + return TRUE; + +} /* check_runlevel() */ + + + +/* + * adjust_cwd() - this function scans through program_name (ie + * argv[0]) for any possible relative paths, and chdirs into the + * relative path it finds. The point of all this is to make the + * directory with the executed binary the cwd. + * + * It is assumed that the user interface has not yet been initialized + * by the time this function is called. + */ + +int adjust_cwd(Options *op, const char *program_name) +{ + char *c, *path; + int len; + + /* + * extract any pathname portion out of the program_name and chdir + * to it + */ + + c = strrchr(program_name, '/'); + if (c) { + len = c - program_name + 1; + path = (char *) nvalloc(len + 1); + strncpy(path, program_name, len); + path[len] = '\0'; + if (op->expert) log_printf(op, TRUE, NULL, "chdir(\"%s\")", path); + if (chdir(path)) { + fprintf(stderr, "Unable to chdir to %s (%s)", + path, strerror(errno)); + return FALSE; + } + free(path); + } + + return TRUE; + +} /* adjust_cwd() */ + + + +/* + * fget_next_line() - read from the given FILE stream until a newline, + * EOF, or null terminator is encountered, writing data into a + * growable buffer. The eof parameter is set to TRUE when EOF is + * encountered. In all cases, the returned string is null-terminated. + * + * XXX this function will be rather slow because it uses fgetc() to + * pull each character off the stream one at a time; this is done so + * that each character can be examined as it's read so that we can + * appropriately deal with EOFs and newlines. A better implementation + * would use fgets(), but that would still require us to parse each + * read line, checking for newlines or guessing if we hit an EOF. + */ + +char *fget_next_line(FILE *fp, int *eof) +{ + char *buf = NULL, *tmpbuf; + char *c = NULL; + int len = 0, buflen = 0; + + if (eof) *eof = FALSE; + + while (1) { + if (buflen == len) { /* buffer isn't big enough -- grow it */ + buflen += NV_LINE_LEN; + tmpbuf = (char *) nvalloc (buflen); + if (buf) { + memcpy (tmpbuf, buf, len); + free (buf); + } + buf = tmpbuf; + c = buf + len; + } + + *c = fgetc(fp); + + if ((*c == EOF) && (eof)) *eof = TRUE; + if ((*c == EOF) || (*c == '\n') || (*c == '\0')) { + *c = '\0'; + return buf; + } + + len++; + c++; + + } /* while (1) */ + + return NULL; /* should never get here */ + +} /* fget_next_line() */ + + + +/* + * get_next_line() - this function scans for the next newline or + * carriage return in buf. If non-NULL, the passed-by-reference + * parameter e is set to point to the next printable character in the + * buffer, or NULL if EOF is encountered. + * + * On success, a newly allocated buffer is allocated containing the + * next line of text (with a NULL terminator in place of the + * newline/carriage return). + * + * On error, NULL is returned. + */ + +char *get_next_line(char *buf, char **e) +{ + char *c, *retbuf; + int len; + + if (e) *e = NULL; + + if ((!buf) || (*buf == '\0') || (*buf == EOF)) return NULL; + + c = buf; + while ((*c != '\0') && (*c != EOF) && (*c != '\n') && (*c != '\r')) c++; + + len = c - buf; + retbuf = nvalloc(len + 1); + strncpy(retbuf, buf, len); + retbuf[len] = '\0'; + + if (e) { + while ((*c != '\0') && (*c != EOF) && (!isprint(*c))) c++; + if ((*c == '\0') || (*c == EOF)) *e = NULL; + else *e = c; + } + + return retbuf; + +} /* get_next_line() */ + + + +/* + * run_command() - this function runs the given command and assigns + * the data parameter to a malloced buffer containing the command's + * output, if any. The caller of this function should free the data + * string. The return value of the command is returned from this + * function. + * + * The output parameter controls whether command output is sent to the + * ui; if this is TRUE, then everyline of output that is read is sent + * to the ui. + * + * If the status parameter is greater than 0, it is interpretted as a + * rough estimate of how many lines of output will be generated by the + * command. This is used to compute the value that should be passed + * to ui_status_update() for every line of output that is received. + * + * The redirect argument tells run_command() to redirect stderr to + * stdout so that all output is collected, or just stdout. + * + * XXX maybe we should do something to cap the time we allow the + * command to run? + */ + +int run_command(Options *op, const char *cmd, char **data, int output, + int status, int redirect) +{ + int n, len, buflen, ret; + char *cmd2, *buf, *tmpbuf; + FILE *stream = NULL; + float percent; + + if (data) *data = NULL; + + /* + * if command output is requested, print the command that we will + * execute + */ + + if (output) ui_command_output (op, "executing: '%s'...", cmd); + + /* redirect stderr to stdout */ + + if (redirect) { + cmd2 = nvstrcat(cmd, " 2>&1", NULL); + } else { + cmd2 = nvstrdup(cmd); + } + + /* + * Open a process by creating a pipe, forking, and invoking the + * command. + */ + + if ((stream = popen(cmd2, "r")) == NULL) { + ui_error(op, "Failure executing command '%s' (%s).", + cmd, strerror(errno)); + return errno; + } + + free(cmd2); + + /* + * read from the stream, filling and growing buf, until we hit + * EOF. Send each line to the ui as it is read. + */ + + len = 0; /* length of what has actually been read */ + buflen = 0; /* length of destination buffer */ + buf = NULL; + n = 0; /* output line counter */ + + while (1) { + + if ((buflen - len) < NV_MIN_LINE_LEN) { + buflen += NV_LINE_LEN; + tmpbuf = (char *) nvalloc(buflen); + if (buf) { + memcpy(tmpbuf, buf, len); + free(buf); + } + buf = tmpbuf; + } + + if (fgets(buf + len, buflen - len, stream) == NULL) break; + + if (output) ui_command_output(op, buf + len); + + len += strlen(buf + len); + + if (status) { + n++; + if (n > status) n = status; + percent = (float) n / (float) status; + ui_status_update(op, percent, NULL); + } + } /* while (1) */ + + /* Close the popen()'ed stream. */ + + ret = pclose(stream); + + /* if the last character in the buffer is a newline, null it */ + + if ((len > 0) && (buf[len-1] == '\n')) buf[len-1] = '\0'; + + if (data) *data = buf; + else free(buf); + + return ret; + +} /* run_command() */ + + + +/* + * find_system_utils() - search the $PATH (as well as some common + * additional directories) for the utilities that the installer will + * need to use. Returns TRUE on success and assigns the util fields + * in the option struct; it returns FALSE on failure. + * + * XXX requiring ld may cause problems + */ + +#define EXTRA_PATH "/bin:/usr/bin:/sbin:/usr/sbin" + +int find_system_utils(Options *op) +{ + /* keep in sync with the Utils enum type */ + const struct { char *util, *package; } needed_utils[] = { + { "insmod", "modutils" }, + { "modprobe", "modutils" }, + { "rmmod", "modutils" }, + { "lsmod", "modutils" }, + { "depmod", "modutils" }, + { "ldconfig", "glibc" }, + { "ld", "binutils" }, + { "objcopy", "binutils" }, + }; + + int i; + + ui_expert(op, "Searching for system utilities:"); + + /* search the PATH for each utility */ + + for (i = 0; i < MAX_UTILS; i++) { + op->utils[i] = find_system_util(needed_utils[i].util); + if (!op->utils[i]) { + ui_error(op, "Unable to find the system utility `%s`; please " + "make sure you have the package '%s' installed. If " + "you do have %s installed, then please check that " + "`%s` is in your PATH.", + needed_utils[i].util, needed_utils[i].package, + needed_utils[i].package, needed_utils[i].util); + return FALSE; + } + + ui_expert(op, "found `%s` : `%s`", + needed_utils[i].util, op->utils[i]); + } + + return TRUE; + +} /* find_system_utils() */ + + + +/* + * find_system_util() - build a search path and search for the named + * utility. If the utility is found, the fully qualified path to the + * utility is returned. On failure NULL is returned. + */ + +static char *find_system_util(char *util) +{ + char *buf, *path, *file, *x, *y; + + /* build the search path */ + + buf = getenv("PATH"); + if (buf) { + path = nvstrcat(buf, ":", EXTRA_PATH, NULL); + } else { + path = nvstrdup(EXTRA_PATH); + } + + /* search the PATH for the utility */ + + for (x = y = path; *x; x++) { + if (*x == ':') { + *x = '\0'; + file = nvstrcat(y, "/", util, NULL); + *x = ':'; + if ((access(file, F_OK | X_OK)) == 0) { + nvfree(path); + return file; + } + nvfree(file); + y = x + 1; + } + } + + nvfree(path); + + return NULL; + +} /* find_system_util() */ + + + +/* + * nvid_version() - parse the given nvid string for the version + * number, and assign major, minor and patch. Returns TRUE on + * success, FALSE on failure. + * + * The version format is assumed to be: is X.Y-ZZZZ + */ + +int nvid_version (const char *str, int *major, int *minor, int *patch) +{ + char *s, *x; + int ret = FALSE; + + s = nvstrdup(str); + x = s; + + while (*x) { + if (((x[0]) && isdigit(x[0])) && + ((x[1]) && (x[1] == '.')) && + ((x[2]) && isdigit(x[2])) && + ((x[3]) && (x[3] == '-')) && + ((x[4]) && isdigit(x[4])) && + ((x[5]) && isdigit(x[5])) && + ((x[6]) && isdigit(x[6])) && + ((x[7]) && isdigit(x[7]))) { + + x[1] = x[3] = x[8] = '\0'; + + *major = atoi(&x[0]); + *minor = atoi(&x[2]); + *patch = atoi(&x[4]); + + ret = TRUE; + break; + } + x++; + } + + free(s); + return ret; + +} /* nvid_version() */ + + + +/* + * continue_after_error() - tell the user that an error has occured, + * and ask them if they would like to continue. + * + * Returns TRUE if the installer should continue. + */ + +int continue_after_error(Options *op, const char *fmt, ...) +{ + char *msg; + int ret; + va_list ap; + + va_start (ap, fmt); + msg = assemble_string (fmt, ap); + va_end (ap); + + ret = ui_yes_no(op, TRUE, "The installer has encountered the following " + "error during installation: '%s'. Continue anyway? " + "(\"no\" will abort)?", msg); + + return ret; + +} /* continue_after_error() */ + + + +/* + * do_install() + */ + +int do_install(Options *op, Package *p, CommandList *c) +{ + char *msg; + int len, ret; + + len = strlen(p->description) + strlen(p->version_string) + 64; + msg = (char *) nvalloc(len); + snprintf(msg, len, "Installing '%s' (%s):", + p->description, p->version_string); + + ret = execute_command_list(op, c, msg, "Installing"); + + free(msg); + + if (!ret) return FALSE; + + ui_log(op, "Driver file installation is complete.", p->description); + + return TRUE; + +} /* do_install() */ + + + +/* + * should_install_opengl_headers() - if in expert mode, ask the user + * if they want to install OpenGL header files. + */ + +void should_install_opengl_headers(Options *op, Package *p) +{ + int i, have_headers = FALSE; + + if (!op->expert) return; + + /* + * first, scan through the package to see if we have any header + * files to install + */ + + for (i = 0; i < p->num_entries; i++) { + if (p->entries[i].flags & FILE_TYPE_OPENGL_HEADER) have_headers = TRUE; + } + + if (!have_headers) return; + + /* + * If we're to provide more verbose descriptions, we could present + * something like this: + * + * ("The %s provides OpenGL header files; these are used when + * compiling OpenGL applications. Most Linux distributions + * already have OpenGL header files installed (normally in the + * /usr/include/GL/ directory). If you don't have OpenGL header + * files installed and would like to, or if you want to develop + * OpenGL applications that take advantage of NVIDIA OpenGL + * extensions, then you can install NVIDIA's OpenGL header files + * at this time.", p->description); + */ + + op->opengl_headers = ui_yes_no(op, op->opengl_headers, + "Install NVIDIA's OpenGL header files?"); + + ui_expert(op, "Installation %s install the OpenGL header files.", + op->opengl_headers ? "will" : "will not"); + +} /* should_install_opengl_headers() */ + + + +/* + * check_installed_files_from_package() - scan through the entries in + * the package, making sure that all symbolic links and files are + * properly installed. + */ + +void check_installed_files_from_package(Options *op, Package *p) +{ + int i, ret = TRUE; + float percent; + unsigned int installable_files; + + ui_status_begin(op, "Running post-install sanity check:", "Checking"); + + installable_files = get_installable_file_mask(op); + + for (i = 0; i < p->num_entries; i++) { + + percent = (float) i / (float) p->num_entries; + ui_status_update(op, percent, p->entries[i].dst); + + if (p->entries[i].flags & FILE_TYPE_SYMLINK) { + if (!check_symlink(op, p->entries[i].target, + p->entries[i].dst, + p->description)) { + ret = FALSE; + } + } else if (p->entries[i].flags & installable_files) { + if (!check_file(op, p->entries[i].dst, p->entries[i].mode, 0)) { + ret = FALSE; + } + } + } + + ui_status_end(op, "done."); + + if (ret) { + ui_log(op, "Sanity check passed."); + } else { + ui_log(op, "The sanity check found some discrepancies."); + } +} /* check_installed_files_from_package() */ + + + +/* + * check_symlink() - check that the specified symbolic link exists and + * point to the correct target. Print descriptive warnings if + * anything about the symbolic link doesn't appear as it should. + * + * Returns FALSE if the symbolic link appeared wrong; returns TRUE if + * everything appears in order. + */ + +static int check_symlink(Options *op, const char *target, const char *link, + const char *descr) +{ + char *actual_target; + + actual_target = get_symlink_target(op, link); + if (!actual_target) { + ui_warn(op, "The symbolic link '%s' does not exist. This is " + "necessary for correct operation of the %s. You can " + "create this symbolic link manually by executing " + "`ln -sf %s %s`.", + link, + descr, + target, + link); + return FALSE; + } + + if (strcmp(actual_target, target) != 0) { + ui_warn(op, "The symbolic link '%s' does not point to '%s' " + "as is necessary for correct operation of the %s. " + "It is possible that `ldconfig` has created this " + "incorrect symbolic link because %s's " + "\"soname\" conflicts with that of %s. It is " + "recommended that you remove or rename the file " + "'%s' and create the necessary symbolic link by " + "running `ln -sf %s %s`.", + link, + target, + descr, + actual_target, + target, + actual_target, + target, + link); + free(actual_target); + return FALSE; + } + return TRUE; + +} /* check_symlink() */ + + + +/* + * check_file() - check that the specified installed file exists, has + * the correct permissions, and has the correct crc. + * + * If anything is incorrect, print a warning and return FALSE, + * otherwise return TRUE. + */ + +static int check_file(Options *op, const char *filename, + const mode_t mode, const uint32 crc) +{ + struct stat stat_buf; + uint32 actual_crc; + + if (lstat(filename, &stat_buf) == -1) { + ui_warn(op, "Unable to find installed file '%s' (%s).", + filename, strerror(errno)); + return FALSE; + } + + if (!S_ISREG(stat_buf.st_mode)) { + ui_warn(op, "The installed file '%s' is not of the correct filetype.", + filename); + return FALSE; + } + + if ((stat_buf.st_mode & PERM_MASK) != (mode & PERM_MASK)) { + ui_warn(op, "The installed file '%s' has permissions %04o, but it " + "was installed with permissions %04o.", filename, + (stat_buf.st_mode & PERM_MASK), + (mode & PERM_MASK)); + return FALSE; + } + + /* only check the crc if we were handed a non-emtpy crc */ + + if (crc != 0) { + actual_crc = compute_crc(op, filename); + if (crc != actual_crc) { + ui_warn(op, "The installed file '%s' has a different checksum " + "(%ul) than when it was installed (%ul).", + filename, actual_crc, crc); + return FALSE; + } + } + + return TRUE; + +} /* check_file() */ + + + +/* + * get_installable_file_mask() - return the mask of what file types + * should be considered installable. + */ + +unsigned int get_installable_file_mask(Options *op) +{ + unsigned int installable_files = FILE_TYPE_INSTALLABLE_FILE; + if (!op->opengl_headers) installable_files &= ~FILE_TYPE_OPENGL_HEADER; + + return installable_files; + +} /* get_installable_file_mask() */ + + + +/* + * tls_test() - Starting with glibc 2.3, there is a new thread local + * storage mechanism. To accomodate this, NVIDIA's OpenGL libraries + * are built both the "classic" way, and the new way. To determine + * which set of OpenGL libraries to install, execute the test program + * stored in tls_test_array. If the program returns 0 we should + * install the new tls libraries; if it returns anything else, we + * should install the "classic" libraries. + * + * So as to avoid any risk of not being able to find the tls_test + * binary at run time, the test program is stored as static data + * inside the installer binary (in the same way that the user + * interface shared libraries are)... see + * user_interface.c:extract_user_interface() for details. + * + * Return TRUE if the new tls libraries should be installed; FALSE if + * the old libraries should be used. + */ + +/* pull in the array and size from g_tls_test.c */ + +extern const unsigned char tls_test_array[]; +extern const int tls_test_array_size; + +/* pull in the array and size from g_tls_test_dso.c */ + +extern const unsigned char tls_test_dso_array[]; +extern const int tls_test_dso_array_size; + + + +#if defined(NV_X86_64) + +/* pull in the array and size from g_tls_test_32.c */ + +extern const unsigned char tls_test_array_32[]; +extern const int tls_test_array_32_size; + +/* pull in the array and size from g_tls_test_dso_32.c */ + +extern const unsigned char tls_test_dso_array_32[]; +extern const int tls_test_dso_array_32_size; + +#endif /* NV_X86_64 */ + + +/* forward prototype */ + +static int tls_test_internal(Options *op, + const unsigned char *test_array, + const int test_array_size, + const unsigned char *dso_test_array, + const int dso_test_array_size); + + + +int tls_test(Options *op, int compat_32_libs) +{ + if (compat_32_libs) { + +#if defined(NV_X86_64) + return tls_test_internal(op, + tls_test_array_32, + tls_test_array_32_size, + tls_test_dso_array_32, + tls_test_dso_array_32_size); +#else + return FALSE; +#endif /* NV_X86_64 */ + + } else { + return tls_test_internal(op, + tls_test_array, + tls_test_array_size, + tls_test_dso_array, + tls_test_dso_array_size); + } +} /* tls_test */ + + + +/* + * tls_test_internal() - this is the routine that does all the work to + * write the tests to file and execute them; the caller (tls_test()) + * just selects which array data is used as the test. + */ + +static int tls_test_internal(Options *op, + const unsigned char *test_array, + const int test_array_size, + const unsigned char *test_dso_array, + const int test_dso_array_size) +{ + int ret = FALSE; + char *tmpfile = NULL, *dso_tmpfile = NULL, *cmd = NULL; + + /* allow commandline options to bypass this test */ + + if (op->which_tls == FORCE_NEW_TLS) return TRUE; + if (op->which_tls == FORCE_CLASSIC_TLS) return FALSE; + + /* check that we have the test program */ + + if ((test_array == NULL) || + (test_array_size == 0) || + (test_dso_array == NULL) || + (test_dso_array_size == 0)) { + ui_warn(op, "The thread local storage test program is not " + "present; assuming classic tls."); + return FALSE; + } + + /* write the tls_test data to tmp files */ + + tmpfile = write_temp_file(op, test_array_size, test_array, + S_IRUSR|S_IWUSR|S_IXUSR); + + if (!tmpfile) { + ui_warn(op, "Unable to create temporary file for thread local " + "storage test program (%s); assuming classic tls.", + strerror(errno)); + goto done; + } + + dso_tmpfile = write_temp_file(op, test_dso_array_size, + test_dso_array, + S_IRUSR|S_IWUSR|S_IXUSR); + if (!dso_tmpfile) { + ui_warn(op, "Unable to create temporary file for thread local " + "storage test program (%s); assuming classic tls.", + strerror(errno)); + goto done; + } + + /* run the test */ + + cmd = nvstrcat(tmpfile, " ", dso_tmpfile, NULL); + + ret = run_command(op, cmd, NULL, FALSE, 0, TRUE); + + ret = ((ret == 0) ? TRUE : FALSE); + + done: + + if (tmpfile) { + unlink(tmpfile); + nvfree(tmpfile); + } + + if (dso_tmpfile) { + unlink(dso_tmpfile); + nvfree(dso_tmpfile); + } + + if (cmd) nvfree(cmd); + + return ret; + +} /* test_tls_internal() */ + + + +/* + * get_distribution() - determine what distribution this is; only used + * for several bits of distro-specific behavior requested by + * distribution maintainers. + * + * XXX should we provide a commandline option to override this + * detection? + */ + +Distribution get_distribution(Options *op) +{ + if (access("/etc/SuSE-release", F_OK) == 0) return SUSE; + if (access("/etc/UnitedLinux-release", F_OK) == 0) return UNITED_LINUX; + + return OTHER; + +} /* get_distribution() */ + + + +/* + * check_for_running_x() - running any X server (even with a + * non-NVIDIA driver) can cause stability problems, so check that + * there is no X server running. To do this, scan for any + * /tmp/.X[n]-lock files, where [n] is the number of the X Display + * (we'll just check for 0-7). If any X server is running, print an + * error message and return FALSE. If no X server is running, return + * TRUE. + */ + +int check_for_running_x(Options *op) +{ + char path[14]; + int i; + + /* + * If we are installing for a non-running kernel *and* we are only + * installing a kernel module, then skip this check. + */ + + if (op->kernel_module_only && op->kernel_name) { + ui_log(op, "Only installing a kernel module for a non-running " + "kernel; skipping the \"is an X server running?\" test."); + return TRUE; + } + + for (i = 0; i < 8; i++) { + snprintf(path, 14, "/tmp/.X%1d-lock", i); + if (access(path, R_OK) == 0) { + ui_log(op, "The file '%s' exists... an X server appears to be " + "running", path); + ui_error(op, "You appear to be running an X server; please exit " + "X before installing. For further details, please see " + "the section INSTALLING THE NVIDIA DRIVER in the README " + "available on the Linux driver download page at " + "www.nvidia.com."); + return FALSE; + } + } + + return TRUE; + +} /* check_for_running_x() */ + + +/* + * nv_format_text_rows() - this function breaks the given string str + * into some number of rows, where each row is not longer than the + * specified width. + * + * If prefix is non-NULL, the first line is prepended with the prefix, + * and subsequent lines are indented to line up with the prefix. + * + * If word_boundary is TRUE, then attempt to only break lines on + * boundaries between words. + * + * XXX Note that we don't use nvalloc() or any of the other wrapper + * functions from here, so that this function doesn't require any + * non-c library symbols (so that it can be called from dlopen()'ed + * user interfaces. + */ + +TextRows *nv_format_text_rows(const char *prefix, const char *str, + int width, int word_boundary) +{ + int len, prefix_len, z, w, i; + char *line, *buf, *local_prefix, *a, *b, *c; + TextRows *t; + + /* initialize the TextRows structure */ + + t = (TextRows *) malloc(sizeof(TextRows)); + t->t = NULL; + t->n = 0; + t->m = 0; + + if (!str) return t; + + buf = strdup(str); + + z = strlen(buf); /* length of entire string */ + a = buf; /* pointer to the start of the string */ + + /* initialize the prefix fields */ + + if (prefix) { + prefix_len = strlen(prefix); + local_prefix = nvstrdup(prefix); + } else { + prefix_len = 0; + local_prefix = NULL; + } + + /* adjust the max width for any prefix */ + + w = width - prefix_len; + + do { + /* + * if the string will fit on one line, point b to the end of the + * string + */ + + if (z < w) b = a + z; + + /* + * if the string won't fit on one line, move b to where the + * end of the line should be, and then move b back until we + * find a space; if we don't find a space before we back b all + * the way up to a, just assign b to where the line should end. + */ + + else { + b = a + w; + + if (word_boundary) { + while ((b >= a) && (!isspace(*b))) b--; + if (b <= a) b = a + w; + } + } + + /* look for any newline inbetween a and b, and move b to it */ + + for (c = a; c < b; c++) if (*c == '\n') { b = c; break; } + + /* + * copy the string that starts at a and ends at b, prepending + * with a prefix, if present + */ + + len = b-a; + len += prefix_len; + line = (char *) malloc(len+1); + if (local_prefix) strncpy(line, local_prefix, prefix_len); + strncpy(line + prefix_len, a, len - prefix_len); + line[len] = '\0'; + + /* append the new line to the array of text rows */ + + t->t = (char **) realloc(t->t, sizeof(char *) * (t->n + 1)); + t->t[t->n] = line; + t->n++; + + if (t->m < len) t->m = len; + + /* + * adjust the length of the string and move the pointer to the + * beginning of the new line + */ + + z -= (b - a + 1); + a = b + 1; + + /* move to the first non whitespace character (excluding newlines) */ + + if (word_boundary && isspace(*b)) { + while ((z) && (isspace(*a)) && (*a != '\n')) a++, z--; + } else { + if (!isspace(*b)) z++, a--; + } + + if (local_prefix) { + for (i = 0; i < prefix_len; i++) local_prefix[i] = ' '; + } + + } while (z > 0); + + if (local_prefix) free(local_prefix); + free(buf); + + return t; + +} /* nv_format_text_rows() */ + + + +/* + * nv_free_text_rows() - free the TextRows data structure allocated by + * nv_format_text_rows() + */ + +void nv_free_text_rows(TextRows *t) +{ + int i; + + if (!t) return; + for (i = 0; i < t->n; i++) free(t->t[i]); + if (t->t) free(t->t); + free(t); + +} /* nv_free_text_rows() */ @@ -0,0 +1,68 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * misc.h + */ + +#ifndef __NVIDIA_INSTALLER_MISC_H__ +#define __NVIDIA_INSTALLER_MISC_H__ + +#include <stdio.h> +#include <stdarg.h> + +#include "nvidia-installer.h" +#include "command-list.h" + +void *nvalloc(size_t size); +void *nvrealloc(void *ptr, size_t size); +char *nvstrdup(const char *s); +void nvfree(char *s); +char *nvstrtolower(char *s); +char *nvstrcat(const char *str, ...); +char *read_next_word (char *buf, char **e); +char *assemble_string(const char *fmt, va_list ap); + +int check_euid(Options *op); +int check_runlevel(Options *op); +int adjust_cwd(Options *op, const char *program_name); +char *fget_next_line(FILE *fp, int *eof); +char *get_next_line(char *buf, char **e); +int run_command(Options *op, const char *cmd, char **data, + int output, int status, int redirect); +int find_system_utils(Options *op); +int nvid_version (const char *str, int *major, int *minor, int *patch); +int continue_after_error(Options *op, const char *fmt, ...); +int do_install(Options *op, Package *p, CommandList *c); +void should_install_opengl_headers(Options *op, Package *p); +void check_installed_files_from_package(Options *op, Package *p); +unsigned int get_installable_file_mask(Options *op); +int tls_test(Options *op, int compat_32_libs); +Distribution get_distribution(Options *op); +int check_for_running_x(Options *op); + +TextRows *nv_format_text_rows(const char *prefix, const char *buf, + int width, int word_boundary); +void nv_free_text_rows(TextRows *t); + +#endif /* __NVIDIA_INSTALLER_MISC_H__ */ diff --git a/mkprecompiled.c b/mkprecompiled.c new file mode 100644 index 0000000..f9a68ed --- /dev/null +++ b/mkprecompiled.c @@ -0,0 +1,744 @@ +/* + * gcc mkprecompiled.c -o mkprecompiled -Wall -g + * + * mkprecompiled - this program packages up a precompiled kernel + * module interface with a list of unresolved symbols in the kernel + * module. + * + * normally, this would be done much more simply with a perl or shell + * script, but I've implemented it in C because we don't want the + * installer to rely upon any system utilities that it doesn't + * absolutely need. + * + * commandline options: + * + * -i, --interface=<filename> + * -o, --output=<filename> + * + * --major-version=<major> + * --minor-version=<minor> + * --patch-version=<patch> + * + * -u, --unpack=<filename> + * + * -d, --description=<kernel description> + * + * There is nothing specific to the NVIDIA graphics driver in this + * program, so it should be usable for the nforce drivers, for + * example. + * + * The format of a precompiled kernel interface package is: + * + * the first 8 bytes are: "NVIDIA " + * + * the next 4 bytes (unsigned) are: CRC of the kernel interface module + * + * the next 4 bytes (unsigned) are: major version + * + * the next 4 bytes (unsigned) are: minor version + * + * the next 4 bytes (unsigned) are: patch version + * + * the next 4 bytes (unsigned) are: the length of the description (n) + * + * the next n bytes are the description + * + * the next 4 bytes (unsigned) are: the length of the proc version string (m) + * + * the next m bytes are the proc version string + * + * the rest of the file is the kernel interface module + */ + +#define BINNAME "mkprecompiled" +#define NV_LINE_LEN 256 +#define NV_VERSION_LEN 4096 +#define PROC_VERSION "/proc/version" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <ctype.h> +#include <stdarg.h> + +#define _GNU_SOURCE /* XXX not portable */ +#include <getopt.h> + +#define CONSTANT_LENGTH (8 + 4 + 12 + 4 + 4) + +typedef unsigned int uint32; +typedef unsigned char uint8; + +/* + * Options structure + */ + +typedef struct { + char *interface; + char *output; + char *unpack; + char *description; + char *proc_version_string; + uint32 major; + uint32 minor; + uint32 patch; + uint8 info; + uint8 match; +} Options; + + +#include "crc.h" + + +/* + * nv_alloc() - malloc wrapper that checks for errors, and zeros out + * the memory; if an error occurs, an error is printed to stderr and + * exit() is called -- this function will only return on success. + */ + +void *nv_alloc (size_t size) +{ + void *m = malloc (size); + + if (!m) { + fprintf (stderr, "%s: memory allocation failure\n", BINNAME); + exit (1); + } + memset (m, 0, size); + return (m); + +} /* nv_alloc() */ + + +/* + * XXX hack to resolve symbols used by crc.c + */ + +void *nvalloc(size_t size) +{ + return nv_alloc(size); +} + +void ui_warn(Options *op, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + + + +/* + * nv_open() - open(2) wrapper; prints an error message if open(2) + * fails and calls exit(). This function only returns on success. + */ + +int nv_open(const char *pathname, int flags, mode_t mode) +{ + int fd; + fd = open(pathname, flags, mode); + if (fd == -1) { + fprintf(stderr, "Failure opening %s (%s).\n", + pathname, strerror(errno)); + exit(1); + } + return fd; + +} /* nv_name() */ + + + +/* + * nv_get_file_length() - stat(2) wrapper; prints an error message if + * the system call fails and calls exit(). This function only returns + * on success. + */ + +int nv_get_file_length(const char *filename) +{ + struct stat stat_buf; + int ret; + + ret = stat(filename, &stat_buf); + if (ret == -1) { + fprintf(stderr, "Unable to determine '%s' file length (%s).\n", + filename, strerror(errno)); + exit(1); + } + return stat_buf.st_size; + +} /* nv_get_file_length() */ + + + +/* + * nv_set_file_length() - wrapper for lseek() and write(); prints an + * error message if the system calls fail and calls exit(). This + * function only returns on success. + */ + +void nv_set_file_length(const char *filename, int fd, int len) +{ + if ((lseek(fd, len - 1, SEEK_SET) == -1) || + (write(fd, "", 1) == -1)) { + fprintf(stderr, "Unable to set file '%s' length %d (%s).\n", + filename, fd, strerror(errno)); + exit(1); + } +} /* nv_set_file_length() */ + + + +/* + * nv_mmap() - mmap(2) wrapper; prints an error message if mmap(2) + * fails and calls exit(). This function only returns on success. + */ + +void *nv_mmap(const char *filename, size_t len, int prot, int flags, int fd) +{ + void *ret; + + ret = mmap(0, len, prot, flags, fd, 0); + if (ret == (void *) -1) { + fprintf(stderr, "Unable to mmap file %s (%s).\n", + filename, strerror(errno)); + exit(1); + } + return ret; + +} /* nv_mmap() */ + + + +/* + * print_help() + */ + +void print_help(void) +{ + printf("\n%s [options] \n\n", BINNAME); + + printf("-i, --interface=<interface name>\n"); + printf(" Name of kernel interface file.\n\n"); + + printf("-o, --output=<output name>\n"); + printf(" Name of output file.\n\n"); + + printf("-u, --unpack=<filename>\n"); + printf(" Name of file to be unpacked.\n\n"); + + printf("-d, --description=<kernel description>\n"); + printf(" Kernel description.\n\n"); + + printf("-v, --proc-version=<string>\n"); + printf(" /proc/version string for target kernel.\n\n"); + + printf("--major=<major version number>\n"); + printf("--minor=<minor version number>\n"); + printf("--patch=<patch version number>\n\n"); + + printf("--info\n"); + printf(" Print the description and version number of the file\n"); + printf(" specified by the unpack option.\n\n"); + + printf("-m, --match\n"); + printf(" Check if the precompiled package matches the running\n"); + printf(" kernel.\n\n"); + + printf("This program can be used to either pack a precompiled kernel\n"); + printf("module interface, or unpack it.\n\n"); + +} /* print_help() */ + + + +/* + * parse_commandline() - parse the commandline arguments. do some + * trivial validation, and return an initialized malloc'ed Options + * structure. + */ + +Options *parse_commandline(int argc, char *argv[]) +{ + Options *op; + int c, option_index = 0; + +#define MAJOR_VERSION_OPTION 1 +#define MINOR_VERSION_OPTION 2 +#define PATCH_VERSION_OPTION 3 +#define INFO_OPTION 4 + + static struct option long_options[] = { + { "interface", 1, 0, 'i' }, + { "output", 1, 0, 'o' }, + { "unpack", 1, 0, 'u' }, + { "description", 1, 0, 'd' }, + { "help", 0, 0, 'h' }, + { "proc-version", 1, 0, 'v' }, + { "major", 1, 0, MAJOR_VERSION_OPTION }, + { "minor", 1, 0, MINOR_VERSION_OPTION }, + { "patch", 1, 0, PATCH_VERSION_OPTION }, + { "info", 0, 0, INFO_OPTION }, + { "match", 0, 0, 'm' }, + { 0, 0, 0, 0 } + }; + + op = (Options *) nv_alloc(sizeof(Options)); + + while (1) { + c = getopt_long (argc, argv, "i:b:o:u:d:hv:m", + long_options, &option_index); + if (c == -1) + break; + + switch(c) { + case 'i': op->interface = optarg; break; + case 'o': op->output = optarg; break; + case 'u': op->unpack = optarg; break; + case 'd': op->description = optarg; break; + case 'h': print_help(); exit(0); break; + case 'v': op->proc_version_string = optarg; break; + case MAJOR_VERSION_OPTION: + op->major = atoi(optarg); break; + case MINOR_VERSION_OPTION: + op->minor = atoi(optarg); break; + case PATCH_VERSION_OPTION: + op->patch = atoi(optarg); break; + case INFO_OPTION: + op->info = 1; break; + case 'm': + op->match = 1; break; + default: + fprintf (stderr, "Invalid commandline, please run `%s --help` " + "for usage information.\n", argv[0]); + exit (0); + } + } + + if (optind < argc) { + fprintf (stderr, "Unrecognized arguments: "); + while (optind < argc) + fprintf (stderr, "%s ", argv[optind++]); + fprintf (stderr, "\n"); + fprintf (stderr, "Invalid commandline, please run `%s --help` for " + "usage information.\n", argv[0]); + exit (0); + } + + /* validate options */ + + if (!op->unpack && !(op->interface && op->proc_version_string)) { + + fprintf (stderr, "Incorrect options specified; please run " + "`%s --help` for usage information.\n", argv[0]); + exit(1); + } + + if (!op->info && !op->match && !op->output) { + fprintf(stderr, "Output file not specified.\n"); + exit(1); + } + + return op; + +} /* parse_commandline() */ + + + +char *read_proc_version(void) +{ + int fd, ret, len, version_len; + char *version, *c = NULL; + + fd = nv_open(PROC_VERSION, O_RDONLY, 0); + + /* + * it would be more convenient if we could just mmap(2) + * /proc/version, but that's not supported, so just read in the + * whole file + */ + + len = version_len = 0; + version = NULL; + + while (1) { + if (version_len == len) { + version_len += NV_VERSION_LEN; + version = realloc(version, version_len); + c = version + len; + } + ret = read(fd, c, version_len - len); + if (ret == -1) { + fprintf(stderr, "Error reading %s (%s).\n", + PROC_VERSION, strerror(errno)); + free(version); + return NULL; + } + if (ret == 0) { + *c = '\0'; + break; + } + len += ret; + c += ret; + } + + /* replace a newline with a null-terminator */ + + c = version; + while ((*c != '\0') && (*c != '\n')) c++; + *c = '\0'; + + return version; + +} /* read_proc_version() */ + + + +/* + * check_match() - read /proc/version, and do a strcmp with str. + * Returns 1 if the strings match, 0 if they don't match. + */ + +int check_match(char *str) +{ + int ret = 0; + char *version = read_proc_version(); + + if (strcmp(version, str) == 0) { + ret = 1; + printf("kernel interface matches.\n"); + } else { + ret = 0; + printf("kernel interface doesn't match.\n"); + } + + free(version); + + return ret; + +} /* check_match() */ + + + +/* + * encode_uint32() - given a uint32, and a 4 byte data buffer, write + * the integer to the data buffer. + */ + +void encode_uint32(uint32 val, uint8 data[4]) +{ + data[0] = ((val >> 0) & 0xff); + data[1] = ((val >> 8) & 0xff); + data[2] = ((val >> 16) & 0xff); + data[3] = ((val >> 24) & 0xff); + +} /* encode_uint32() */ + + + +/* + * decode_uint32() - given an index into a buffer, read the next 4 + * bytes, and build a uint32. + */ + +uint32 decode_uint32(uint8 *buf) +{ + uint32 ret = 0; + + ret += (uint32) buf[3]; + ret <<= 8; + + ret += (uint32) buf[2]; + ret <<= 8; + + ret += (uint32) buf[1]; + ret <<= 8; + + ret += (uint32) buf[0]; + ret <<= 0; + + return ret; + +} /* decode_uint32() */ + + + +/* + * pack() - pack the specified precompiled kernel interface file, + * prepended with a header, the CRC the driver version, a description + * string, and the proc version string. + */ + +int pack(Options *op) +{ + int fd, offset, src_fd; + uint8 *out, *src, data[4]; + uint32 crc; + int description_len, proc_version_len, interface_len, total_len; + + /* + * get the lengths of the description, the proc version string, + * and the interface file. + */ + + description_len = strlen(op->description); + proc_version_len = strlen(op->proc_version_string); + interface_len = nv_get_file_length(op->interface); + + total_len = CONSTANT_LENGTH + + description_len + proc_version_len + interface_len; + + /* compute the crc of the kernel interface */ + + crc = compute_crc(NULL, op->interface); + + /* open the output file for writing */ + + fd = nv_open(op->output, O_CREAT|O_RDWR|O_TRUNC, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + + /* set the output file length */ + + nv_set_file_length(op->output, fd, total_len); + + /* map the input file */ + + out = nv_mmap(op->output, total_len, PROT_READ|PROT_WRITE, + MAP_FILE|MAP_SHARED, fd); + offset = 0; + + /* write the header */ + + memcpy(&(out[0]), "NVIDIA ", 8); + offset += 8; + + /* write the crc */ + + encode_uint32(crc, data); + memcpy(&(out[offset]), data, 4); + offset += 4; + + /* write the version */ + + encode_uint32(op->major, data); + memcpy(&(out[offset]), data, 4); + offset += 4; + + encode_uint32(op->minor, data); + memcpy(&(out[offset]), data, 4); + offset += 4; + + encode_uint32(op->patch, data); + memcpy(&(out[offset]), data, 4); + offset += 4; + + /* write the description */ + + encode_uint32(description_len, data); + memcpy(&(out[offset]), data, 4); + offset += 4; + + if (description_len) { + memcpy(&(out[offset]), op->description, description_len); + offset += description_len; + } + + /* write the proc version string */ + + encode_uint32(proc_version_len, data); + memcpy(&(out[offset]), data, 4); + offset += 4; + + memcpy(&(out[offset]), op->proc_version_string, proc_version_len); + offset += proc_version_len; + + /* open the precompiled kernel module interface for reading */ + + src_fd = nv_open(op->interface, O_RDONLY, 0); + + /* mmap() the kernel module interface */ + + src = nv_mmap(op->interface, interface_len, PROT_READ, + MAP_FILE|MAP_SHARED, src_fd); + + memcpy(out + offset, src, interface_len); + + /* unmap src and dst */ + + munmap(src, interface_len); + munmap(out, total_len); + + close(src_fd); + close(fd); + + return 0; + +} /* pack() */ + + + +/* + * unpack() - unpack the specified package + */ + +int unpack(Options *op) +{ + int dst_fd, fd, ret, offset, len = 0; + uint8 *buf, *dst; + uint32 crc, major, minor, patch, val, size; + char *description, *proc_version_string; + + fd = dst_fd = 0; + buf = dst = NULL; + ret = 1; + + /* open the file to be unpacked */ + + fd = nv_open(op->unpack, O_RDONLY, 0); + + /* get the file length */ + + size = nv_get_file_length(op->unpack); + + /* check for a minimum length */ + + if (size < CONSTANT_LENGTH) { + fprintf(stderr, "File '%s' appears to be too short.\n", op->unpack); + goto done; + } + + /* mmap(2) the input file */ + + buf = nv_mmap(op->unpack, size, PROT_READ, MAP_FILE|MAP_SHARED, fd); + offset = 0; + + /* check for the header */ + + if (strncmp(buf, "NVIDIA ", 8) != 0) { + fprintf(stderr, "File '%s': unrecognized file format.\n", op->unpack); + goto done; + } + offset += 8; + + /* read the crc */ + + crc = decode_uint32(buf + offset); + offset += 4; + + /* read the version */ + + major = decode_uint32(buf + offset + 0); + minor = decode_uint32(buf + offset + 4); + patch = decode_uint32(buf + offset + 8); + offset += 12; + + /* read the description */ + + val = decode_uint32(buf + offset); + offset += 4; + if ((val + CONSTANT_LENGTH) > size) { + fprintf(stderr, "Invalid file.\n"); + goto done; + } + if (val > 0) { + description = nv_alloc(val+1); + memcpy(description, buf + offset, val); + description[val] = '\0'; + } else { + description = NULL; + } + offset += val; + + /* read the proc version string */ + + val = decode_uint32(buf + offset); + offset += 4; + if ((val + CONSTANT_LENGTH) > size) { + fprintf(stderr, "Invalid file.\n"); + goto done; + } + proc_version_string = nv_alloc(val+1); + memcpy(proc_version_string, buf + offset, val); + offset += val; + proc_version_string[val] = '\0'; + + /* + * if info was requested, print the description, driver version, + * crc, proc version, and exit + */ + + if (op->info) { + printf("description: %s\n", description); + printf("version: %d.%d-%d\n", major, minor, patch); + printf("crc: %u\n", crc); + printf("proc version: %s\n", proc_version_string); + return 0; + } + + /* check if the running kernel matches */ + + if (op->match) { + return check_match(proc_version_string); + } + + /* extract kernel interface module */ + + len = size - offset; + + dst_fd = nv_open(op->output, O_CREAT | O_RDWR | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + /* set the output file length */ + + nv_set_file_length(op->output, dst_fd, len); + + /* mmap the dst */ + + dst = nv_mmap(op->output, len, PROT_READ|PROT_WRITE, + MAP_FILE|MAP_SHARED, dst_fd); + + /* copy */ + + memcpy(dst, buf + offset, len); + + ret = 0; + + done: + if (dst) munmap(dst, len); + if (buf) munmap(buf, size); + if (fd > 0) close(fd); + if (dst_fd > 0) close(dst_fd); + return ret; + +} /* unpack() */ + + + +/* + * program entry point + */ + +int main(int argc, char *argv[]) +{ + Options *op; + int ret; + + op = parse_commandline(argc, argv); + + if (op->unpack) { + ret = unpack(op); + } else { /* pack */ + ret = pack(op); + } + + return ret; + +} /* main() */ diff --git a/ncurses-ui.c b/ncurses-ui.c new file mode 100644 index 0000000..ccdf24a --- /dev/null +++ b/ncurses-ui.c @@ -0,0 +1,2139 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * ncurses_ui.c - implementation of the nvidia-installer ui using ncurses. + */ + +#include <ncurses.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> + +#include "nvidia-installer.h" +#include "nvidia-installer-ui.h" + + + + +/* structures */ + +/* + * RegionStruct - a region is sort of like an ncurses window, but + * implemented completely in this source file (ie: doesn't use ncurses + * windows). I found enough bugs with ncurses windows that I felt it + * better to avoid their use altogether. + */ + +typedef struct { + int x, y, w, h; /* position and dimensions relative to stdscr */ + int attr; /* attributes (to be passed to setattr()) */ + char *line; /* a string filled with spaces; its length is the + width of the region. This is just a + convenience for use when clearing the region */ +} RegionStruct; + + + + +/* + * PagerStruct - Pager implements the functionality of `less` + */ + +typedef struct { + TextRows *t; /* rows of text to be displayed */ + RegionStruct *region; /* region in which the text will be displayed */ + const char *label; /* label for the left side of the footer */ + int cur; /* current position in the pager */ + int page; /* height of a page (for use with pgup/pgdn) */ +} PagerStruct; + + + + +/* + * DataStruct - private data structure that gets plugged into + * Options->ui_priv. + */ + +typedef struct { + + RegionStruct *header; /* header region */ + RegionStruct *footer; /* footer region */ + RegionStruct *message; /* message region */ + + FormatTextRows format_text_rows; /* XXX function pointer from installer */ + + bool use_color; /* should the ncurses ui use color? */ + + int width; /* cached copy of the terminal dimensions */ + int height; + + char *title; /* cached strings for the header and footer */ + char *footer_left; + char *footer_right; + + char *progress_title; /* cached string for the title of the + progress messages */ + +} DataStruct; + + + + +/* constants */ + +/* default strings for the header and footer */ + +#define NV_NCURSES_DEFAULT_TITLE "NVIDIA Software Installer for Unix/Linux" +#define NV_NCURSES_DEFAULT_FOOTER_LEFT NV_NCURSES_DEFAULT_TITLE +#define NV_NCURSES_DEFAULT_FOOTER_RIGHT "www.nvidia.com" + +/* indices for color pairs */ + +#define NV_NCURSES_HEADER_COLOR_IDX 1 +#define NV_NCURSES_MESSAGE_COLOR_IDX 2 +#define NV_NCURSES_BUTTON_COLOR_IDX 3 +#define NV_NCURSES_INPUT_COLOR_IDX 4 + + +#define NV_NCURSES_HEADER_COLOR (COLOR_PAIR(NV_NCURSES_HEADER_COLOR_IDX)) +#define NV_NCURSES_FOOTER_COLOR A_REVERSE +#define NV_NCURSES_MESSAGE_COLOR (COLOR_PAIR(NV_NCURSES_MESSAGE_COLOR_IDX)) +#define NV_NCURSES_BUTTON_COLOR (COLOR_PAIR(NV_NCURSES_BUTTON_COLOR_IDX)) +#define NV_NCURSES_INPUT_COLOR (COLOR_PAIR(NV_NCURSES_INPUT_COLOR_IDX)) + +#define NV_NCURSES_HEADER_NO_COLOR A_REVERSE +#define NV_NCURSES_FOOTER_NO_COLOR A_REVERSE +#define NV_NCURSES_MESSAGE_NO_COLOR A_NORMAL + +/* use when animating button presses */ + +#define NV_NCURSES_BUTTON_PRESS_TIME 125000 + +#define NV_NCURSES_TAB 9 +#define NV_NCURSES_ENTER 10 +#define NV_NCURSES_BACKSPACE 8 + + +/* + * somewhat arbitrary minimum values: if the current window size is + * smaller than this, don't attempt to use the ncurses ui. + */ + +#define NV_NCURSES_MIN_WIDTH 40 +#define NV_NCURSES_MIN_HEIGHT 10 + +#define NV_NCURSES_HLINE '_' + + + +/* prototypes for ui entry points */ + +static int nv_ncurses_detect (Options*); +static int nv_ncurses_init (Options*, + FormatTextRows format_text_rows); +static void nv_ncurses_set_title (Options*, const char*); +static char *nv_ncurses_get_input (Options*, const char*, + const char*); +static int nv_ncurses_display_license (Options*, const char*); +static void nv_ncurses_message (Options*, const int level, + const char*); +static void nv_ncurses_command_output (Options*, const char*); +static int nv_ncurses_approve_command_list(Options*, CommandList*, + const char*); +static int nv_ncurses_yes_no (Options*, const int, const char*); +static void nv_ncurses_status_begin (Options*, const char*, + const char*); +static void nv_ncurses_status_update (Options*, const float, + const char*); +static void nv_ncurses_status_end (Options*, const char*); +static void nv_ncurses_close (Options*); + + + +/* helper functions for manipulating the header and footer */ + +static void nv_ncurses_set_header(DataStruct *, const char *); +static void nv_ncurses_set_footer(DataStruct *, const char *, const char *); + + +/* helper functions for manipulating RegionStructs */ + +static RegionStruct *nv_ncurses_create_region(DataStruct *, int, int, + int, int, int, int); +static void nv_ncurses_clear_region(RegionStruct *); +static void nv_ncurses_destroy_region(RegionStruct *); + + +/* helper functions for drawing buttons */ + +static void nv_ncurses_draw_button(DataStruct *, RegionStruct *, + int, int, int, int, char *, bool, bool); +static void nv_ncurses_erase_button(RegionStruct *, int, int, int, int); + + +/* helper functions for drawing regions and stuff */ + +static void nv_ncurses_do_message_region(DataStruct *, const char *, + const char *, int, int); +static void nv_ncurses_do_progress_bar_region(DataStruct *); +static void nv_ncurses_do_progress_bar_message(DataStruct *, const char *, + int, int); + +/* pager functions */ + +static PagerStruct *nv_ncurses_create_pager(DataStruct *, int, int, int, int, + TextRows *, const char *); +static void nv_ncurses_pager_update(DataStruct *, PagerStruct *); +static void nv_ncurses_pager_handle_events(DataStruct *, PagerStruct *, int); +static void nv_ncurses_destroy_pager(PagerStruct *); + + +/* progress bar helper functions */ + +static int choose_char(int i, int p[4], char v[4], char def); +static void init_percentage_string(char v[4], int n); +static void init_position(int p[4], int w); + + +/* misc helper functions */ + +static TextRows *nv_ncurses_create_command_list_textrows(DataStruct *, + CommandList *, int); +static char *nv_ncurses_mode_to_permission_string(mode_t); + +static void nv_ncurses_concat_text_rows(TextRows *, TextRows *); +static void nv_ncurses_free_text_rows(TextRows *); + +static int nv_ncurses_format_print(DataStruct *, RegionStruct *, + int, int, int, int, TextRows *t); + +static int nv_ncurses_check_resize(DataStruct *); + + + + + + + +/* dispatch table that gets dlsym()'ed by ui_init() */ + +InstallerUI ui_dispatch_table = { + nv_ncurses_detect, + nv_ncurses_init, + nv_ncurses_set_title, + nv_ncurses_get_input, + nv_ncurses_display_license, + nv_ncurses_message, + nv_ncurses_command_output, + nv_ncurses_approve_command_list, + nv_ncurses_yes_no, + nv_ncurses_status_begin, + nv_ncurses_status_update, + nv_ncurses_status_end, + nv_ncurses_close +}; + + + + +/* + * nv_ncurses_detect() - initialize ncurses; return FALSE if + * initialization fails + */ + +static int nv_ncurses_detect(Options *op) +{ + int x, y; + + if (!initscr()) return FALSE; + + /* + * query the current size of the window, and don't try to use the + * ncurses ui if it's too small. + */ + + getmaxyx(stdscr, y, x); + + if ((x < NV_NCURSES_MIN_WIDTH) || (y < NV_NCURSES_MIN_HEIGHT)) { + endwin(); + return FALSE; + } + + return TRUE; + +} /* nv_ncurses_detect() */ + + + + +/* + * nv_ncurses_init() - initialize the ncurses interface. + */ + +static int nv_ncurses_init(Options *op, FormatTextRows format_text_rows) +{ + DataStruct *d; + + d = (DataStruct *) malloc(sizeof(DataStruct)); + memset(d, 0, sizeof(DataStruct)); + + d->format_text_rows = format_text_rows; + + /* initialize color */ + + d->use_color = !op->no_ncurses_color; + + if (d->use_color) { + if (!has_colors()) { + d->use_color = FALSE; + } + } + + if (d->use_color) { + if (start_color() == ERR) { + d->use_color = FALSE; + } else { + /* foreground background */ + init_pair(NV_NCURSES_HEADER_COLOR_IDX, COLOR_BLACK, COLOR_GREEN); + init_pair(NV_NCURSES_MESSAGE_COLOR_IDX, COLOR_WHITE, COLOR_BLUE); + init_pair(NV_NCURSES_BUTTON_COLOR_IDX, COLOR_WHITE, COLOR_RED); + init_pair(NV_NCURSES_INPUT_COLOR_IDX, COLOR_GREEN, COLOR_BLACK); + } + } + + clear(); /* clear the screen */ + noecho(); /* don't echo input to the screen */ + cbreak(); /* disable line buffering and control characters */ + curs_set(0); /* make the cursor invisible */ + keypad(stdscr, TRUE); /* enable keypad, function keys, arrow keys, etc */ + + getmaxyx(stdscr, d->height, d->width); /* get current window dimensions */ + + /* create the regions */ + + d->header = nv_ncurses_create_region(d, 1, 0, d->width - 2, 1, + NV_NCURSES_HEADER_COLOR, + NV_NCURSES_HEADER_NO_COLOR); + + d->footer = nv_ncurses_create_region(d, 1, d->height - 2, d->width - 2, 1, + NV_NCURSES_FOOTER_COLOR, + NV_NCURSES_FOOTER_NO_COLOR); + + /* plug the DataStruct struct into the ui_priv pointer */ + + op->ui_priv = (void *) d; + + /* set the initial strings in the header and footer */ + + nv_ncurses_set_header(d, NV_NCURSES_DEFAULT_TITLE); + + nv_ncurses_set_footer(d, NV_NCURSES_DEFAULT_FOOTER_LEFT, + NV_NCURSES_DEFAULT_FOOTER_RIGHT); + + refresh(); + + return TRUE; + +} /* nv_ncurses_init() */ + + + + +/* + * nv_ncurses_set_title() - update the string in the header region + */ + +static void nv_ncurses_set_title(Options *op, const char *title) +{ + DataStruct *d = (DataStruct *) op->ui_priv; + + nv_ncurses_set_header(d, title); + + refresh(); + +} /* nv_ncurses_set_title() */ + + + + +/* + * nv_ncurses_get_input - prompt for user input with the given msg; + * returns the user inputed string. + */ + +#define MIN_INPUT_LEN 32 +#define MAX_BUF_LEN 1024 +#define BUF_CHAR(c) ((c) ? (c) : ' ') + +static char *nv_ncurses_get_input(Options *op, + const char *def, const char *msg) +{ + DataStruct *d = (DataStruct *) op->ui_priv; + int msg_len, width, input_len, buf_len, def_len; + int input_x, input_y, i, w, h, x, y, c, lines, ch, color, redraw; + char *tmp, buf[MAX_BUF_LEN]; + TextRows *t; + + if (!msg) return NULL; + + nv_ncurses_check_resize(d); + + color = d->use_color ? NV_NCURSES_INPUT_COLOR : A_REVERSE; + + /* concatenate ": " to the end of the message */ + + msg_len = strlen(msg) + 2; + tmp = (char *) malloc(msg_len + 1); + snprintf(tmp, msg_len, "%s: ", msg); + + /* copy the default response into the buffer */ + + memset(buf, 0, MAX_BUF_LEN); + if (def) strncpy(buf, def, MAX_BUF_LEN); + + draw_get_input: + + /* free any existing message region */ + + if (d->message) { + nv_ncurses_destroy_region(d->message); + d->message = NULL; + } + + /* + * compute the size of the input box: the input box is twice the + * length of the default string, clamped to MIN_INPUT_LEN + */ + + def_len = def ? strlen(def) : 0; + input_len = NV_MAX(def_len * 2, MIN_INPUT_LEN); + width = d->width - 4; + + /* convert the message to text rows */ + + t = d->format_text_rows(NULL, tmp, width, TRUE); + + /* + * if the message and input box will fit on one line, do that; + * otherwise, place them on separate lines + */ + + if ((msg_len + input_len + 1) < width) { + input_x = 1 + msg_len; + lines = 1; + } else { + input_x = 2; + lines = t->n + 1; + } + + input_y = lines; + + /* + * compute the width, height, and starting position of the message + * region + */ + + w = d->width - 2; + h = lines + 2; + x = 1; + y = ((d->height - (3 + h)) / 3) + 1; + + /* create the message region */ + + d->message = nv_ncurses_create_region(d, x, y, w, h, + NV_NCURSES_MESSAGE_COLOR, + NV_NCURSES_MESSAGE_NO_COLOR); + + nv_ncurses_format_print(d, d->message, 1, 1, d->message->w - 2, lines, t); + + /* free the text rows */ + + nv_ncurses_free_text_rows(t); + + /* clamp the input box to the width of the region */ + + input_len = NV_MIN(input_len, width - 2); + + curs_set(1); /* make the cursor visible */ + + c = buf_len = strlen(buf); + + redraw = TRUE; /* force a redraw the first time through */ + + /* offset input_x and input_y by the region offset */ + + input_x += d->message->x; + input_y += d->message->y; + + do { + x = NV_MAX(c - (input_len - 1), 0); + + /* redraw the input box */ + + if (redraw) { + for (i = 0; i < input_len; i++) { + mvaddch(input_y, input_x + i, BUF_CHAR(buf[i + x]) | color); + } + + /* if we're scrolling, display an arrow */ + + if (x > 0) { + mvaddch(input_y, input_x - 1, '<' | d->message->attr); + } else { + mvaddch(input_y, input_x - 1, ' ' | d->message->attr); + } + + if (buf_len > (input_len - 1 + x)) { + mvaddch(input_y, input_x + input_len, '>' | d->message->attr); + } else { + mvaddch(input_y, input_x + input_len, ' ' | d->message->attr); + } + + redraw = FALSE; + } + + /* position the cursor */ + + move(input_y, input_x - x + c); + refresh(); + + /* wait for input */ + + if (nv_ncurses_check_resize(d)) goto draw_get_input; + ch = getch(); + + switch (ch) { + + case NV_NCURSES_BACKSPACE: + case KEY_BACKSPACE: + + /* + * If there is a character to be deleted, move everything + * after the character forward, and decrement c and len. + */ + + if (c <= 0) break; + for (i = c; i <= buf_len; i++) buf[i-1] = buf[i]; + c--; + buf_len--; + redraw = TRUE; + break; + + case KEY_DC: + + /* + * If there is a character to be deleted, move everything + * after the character forward, and decrement len. + */ + + if (c == buf_len) break; + for (i = c; i < buf_len; i++) buf[i] = buf[i+1]; + buf_len--; + redraw = TRUE; + break; + + case KEY_LEFT: + if (c > 0) { + c--; + redraw = TRUE; + } + break; + + case KEY_RIGHT: + if (c < buf_len) { + c++; + redraw = TRUE; + } + break; + } + + /* + * If we have a printable character, then move everything + * after the current location back, and insert the character. + */ + + if (isprint(ch)) { + if (buf_len < (MAX_BUF_LEN - 1)) { + for (i = buf_len; i > c; i--) buf[i] = buf[i-1]; + buf[c] = (char) ch; + buf_len++; + c++; + redraw = TRUE; + } + } + + if (op->debug) { + mvprintw(d->message->y, d->message->x, + "c: %3d ch: %04o (%d)", c, ch, ch); + clrtoeol(); + redraw = TRUE; + } + + } while (ch != NV_NCURSES_ENTER); + + /* free the message region */ + + nv_ncurses_destroy_region(d->message); + d->message = NULL; + + curs_set(0); /* make the cursor invisible */ + refresh(); + + free(tmp); + tmp = strdup(buf); + return tmp; + +} /* nv_ncurses_get_input() */ + + + + +/* + * nv_ncurses_display_license() - print the text from the license file, + * prompt for acceptance from the user, and return whether or not the + * user accepted. + */ + +static int nv_ncurses_display_license(Options *op, const char *license) +{ + DataStruct *d = (DataStruct *) op->ui_priv; + TextRows *t_pager = NULL; + int ch, x; + int button_width, button_y, accept_x, no_accept_x, accepted; + PagerStruct *p = NULL; + char *str; + + static const char *descr = "Please read the following LICENSE " + "and then select either \"Accept\" to accept the license " + "and continue with the installation, or select " + "\"Do Not Accept\" to abort the installation."; + + nv_ncurses_check_resize(d); + + draw_license: + + /* free the pager, and pager's text rows */ + + if (d->message) nv_ncurses_destroy_region(d->message); + if (p) nv_ncurses_destroy_pager(p); + if (t_pager) nv_ncurses_free_text_rows(t_pager); + + /* create the message region and print descr in it */ + + nv_ncurses_do_message_region(d, NULL, descr, TRUE, 2); + + /* draw the accept buttons */ + + button_width = 15; + button_y = d->message->h - 2; + accept_x = (d->message->w - (button_width * 3)) / 2; + no_accept_x = accept_x + (button_width * 2); + accepted = 0; + + nv_ncurses_draw_button(d, d->message, accept_x, button_y, button_width, + 1, "Accept", accepted, FALSE); + + nv_ncurses_draw_button(d, d->message, no_accept_x, button_y, button_width, + 1, "Do Not Accept", !accepted, FALSE); + + /* init the pager to display the license */ + + t_pager = d->format_text_rows(NULL, license, d->message->w, TRUE); + p = nv_ncurses_create_pager(d, 1, d->message->h + 2, d->message->w, + d->height - d->message->h - 4, + t_pager, "NVIDIA Software License"); + refresh(); + + /* process key strokes */ + + do { + /* if a resize occurred, jump back to the top and redraw */ + + if (nv_ncurses_check_resize(d)) goto draw_license; + ch = getch(); + + switch (ch) { + case NV_NCURSES_TAB: + case KEY_LEFT: + case KEY_RIGHT: + + /* + * any of left, right, and tab will toggle which button is + * selected + */ + + accepted ^= 1; + + nv_ncurses_draw_button(d, d->message, accept_x, button_y, + button_width, 1, "Accept", + accepted, FALSE); + + nv_ncurses_draw_button(d, d->message, no_accept_x, button_y, + button_width, 1, "Do Not Accept", + !accepted, FALSE); + refresh(); + break; + + default: + break; + } + + nv_ncurses_pager_handle_events(d, p, ch); + + } while (ch != NV_NCURSES_ENTER); + + /* animate the button being pushed down */ + + x = accepted ? accept_x : no_accept_x; + str = accepted ? "Accept" : "Do Not Accept"; + + nv_ncurses_erase_button(d->message, x, button_y, button_width, 1); + nv_ncurses_draw_button(d, d->message, x, button_y, button_width, 1, + str, TRUE, TRUE); + refresh(); + usleep(NV_NCURSES_BUTTON_PRESS_TIME); + + nv_ncurses_erase_button(d->message, x, button_y, button_width, 1); + nv_ncurses_draw_button(d, d->message, x, button_y, button_width, 1, + str, TRUE, FALSE); + refresh(); + usleep(NV_NCURSES_BUTTON_PRESS_TIME); + + /* free the text rows used by the pager */ + + nv_ncurses_free_text_rows(t_pager); + + /* free the pager */ + + nv_ncurses_destroy_pager(p); + + /* restore the footer */ + + nv_ncurses_set_footer(d, NV_NCURSES_DEFAULT_FOOTER_LEFT, + NV_NCURSES_DEFAULT_FOOTER_RIGHT); + + /* free the message region */ + + nv_ncurses_destroy_region(d->message); + d->message = NULL; + + refresh(); + + return accepted; + +} /* nv_ncurses_display_license() */ + + + + +/* + * nv_ncurses_message() - print a message + */ + +static void nv_ncurses_message(Options *op, int level, const char *msg) +{ + DataStruct *d = (DataStruct *) op->ui_priv; + int w, h, x, y, ch; + char *prefix; + + if (!msg) return; + + /* XXX for now, log messages are ignored by the ncurses ui */ + + if (level == NV_MSG_LEVEL_LOG) return; + + /* determine the prefix for the message from the message level */ + + switch(level) { + case NV_MSG_LEVEL_MESSAGE: + prefix = NULL; + break; + case NV_MSG_LEVEL_WARNING: + prefix = "WARNING: "; + break; + case NV_MSG_LEVEL_ERROR: + prefix = "ERROR: "; + break; + default: + return; + } + + draw_message: + + if (d->message) { + + /* + * XXX we may already have a message region allocated when we + * enter nv_ncurses_message(): we may have been in the middle + * of displaying a progress bar when we encountered an error + * that we need to report. To deal with this situation, throw + * out the existing message region; nv_ncurses_status_update() + * and nv_ncurses_status_end() will have to recreate their + * message region. + */ + + nv_ncurses_destroy_region(d->message); + d->message = NULL; + } + + /* create the message region and print msg in it */ + + nv_ncurses_do_message_region(d, prefix, msg, FALSE, 2); + + /* init the dimensions of the button */ + + w = 6; + h = 1; + x = (d->message->w - w) / 2; + y = d->message->h - 2; + + /* draw the OK button */ + + nv_ncurses_draw_button(d, d->message, x, y, w, h, "OK", + TRUE, FALSE); + refresh(); + + /* wait for enter */ + + do { + /* if a resize occurred, jump back to the top and redraw */ + + if (nv_ncurses_check_resize(d)) goto draw_message; + ch = getch(); + } while (ch != NV_NCURSES_ENTER); + + /* animate the button being pushed down */ + + nv_ncurses_erase_button(d->message, x, y, w, h); + nv_ncurses_draw_button(d, d->message, x, y, w, h, "OK", TRUE, TRUE); + refresh(); + usleep(NV_NCURSES_BUTTON_PRESS_TIME); + + nv_ncurses_erase_button(d->message, x, y, w, h); + nv_ncurses_draw_button(d, d->message, x, y, w, h, "OK", TRUE, FALSE); + refresh(); + usleep(NV_NCURSES_BUTTON_PRESS_TIME); + + /* free the message region */ + + nv_ncurses_destroy_region(d->message); + d->message = NULL; + refresh(); + +} /* nv_ncurses_message() */ + + + + +/* + * nv_ncurses_command_output() - drop this on the floor, for now. + */ + +static void nv_ncurses_command_output(Options *op, const char *msg) +{ + /* + * XXX we don't currently display command output in the ncurses ui + */ + + return; + +} /* nv_ncurses_command_output() */ + + + + +/* + * nv_ncurses_approve_command_list() - list all the commands that will be + * executed, and ask for approval. + */ + +static int nv_ncurses_approve_command_list(Options *op, CommandList *cl, + const char *descr) +{ + DataStruct *d = (DataStruct *) op->ui_priv; + TextRows *t_pager = NULL; + PagerStruct *p = NULL; + char *str, *question; + int len, ch, x, yes_x, no_x, yes, button_w, button_y; + + /* initialize the question string */ + + len = strlen(descr) + 256; + question = (char *) malloc(len + 1); + snprintf(question, len, "The following operations will be performed to " + "install the %s. Is this acceptable?", descr); + + /* check if the window was resized */ + + nv_ncurses_check_resize(d); + + draw_command_list: + + /* free any existing message region, pager, and pager textrows */ + + if (d->message) { + nv_ncurses_destroy_region(d->message); + d->message = NULL; + } + if (p) nv_ncurses_destroy_pager(p); + if (t_pager) nv_ncurses_free_text_rows(t_pager); + + /* create the message region and print descr in it */ + + nv_ncurses_do_message_region(d, NULL, question, TRUE, 2); + + /* draw the yes/no buttons */ + + button_w = 7; + button_y = d->message->h - 2; + yes_x = (d->message->w - button_w*3)/2 + 0; + no_x = (d->message->w - button_w*3)/2 + 2*button_w; + + yes = 1; + + nv_ncurses_draw_button(d, d->message, yes_x, button_y, + button_w, 1, "Yes", yes, FALSE); + nv_ncurses_draw_button(d, d->message, no_x, button_y, + button_w, 1, "No", !yes, FALSE); + + /* draw the command list */ + + t_pager = nv_ncurses_create_command_list_textrows(d, cl, d->message->w); + p = nv_ncurses_create_pager(d, 1, d->message->h + 2, d->message->w, + d->height - d->message->h - 4, + t_pager, "Proposed Commandlist"); + refresh(); + + /* process key strokes */ + + do { + /* if a resize occurred, jump back to the top and redraw */ + + if (nv_ncurses_check_resize(d)) goto draw_command_list; + ch = getch(); + + switch (ch) { + case NV_NCURSES_TAB: + case KEY_LEFT: + case KEY_RIGHT: + + /* + * any of left, right, and tab will toggle which button is + * selected + */ + + yes ^= 1; + + nv_ncurses_draw_button(d, d->message, yes_x, button_y, + button_w, 1, "Yes", yes, FALSE); + nv_ncurses_draw_button(d, d->message, no_x, button_y, + button_w, 1, "No", !yes, FALSE); + refresh(); + break; + default: + break; + } + + nv_ncurses_pager_handle_events(d, p, ch); + + } while (ch != NV_NCURSES_ENTER); + + /* animate the button being pushed down */ + + x = yes ? yes_x : no_x; + str = yes ? "Yes" : "No"; + + nv_ncurses_erase_button(d->message, x, button_y, button_w, 1); + nv_ncurses_draw_button(d, d->message, x, button_y, + button_w, 1, str, TRUE, TRUE); + refresh(); + usleep(NV_NCURSES_BUTTON_PRESS_TIME); + + nv_ncurses_erase_button(d->message, x, button_y, button_w, 1); + nv_ncurses_draw_button(d, d->message, x, button_y, + button_w, 1, str, TRUE, FALSE); + refresh(); + usleep(NV_NCURSES_BUTTON_PRESS_TIME); + + /* free the text rows used by the pager */ + + nv_ncurses_free_text_rows(t_pager); + + /* free the pager */ + + nv_ncurses_destroy_pager(p); + + /* restore the footer */ + + nv_ncurses_set_footer(d, NV_NCURSES_DEFAULT_FOOTER_LEFT, + NV_NCURSES_DEFAULT_FOOTER_RIGHT); + + /* free the message region */ + + nv_ncurses_destroy_region(d->message); + d->message = NULL; + + refresh(); + + free(question); + + return yes; + +} /* nv_ncurses_approve_command_list() */ + + + + +/* + * nv_ncurses_yes_no() - ask the yes/no question 'msg' and return TRUE for + * yes, and FALSE for no. + */ + +static int nv_ncurses_yes_no(Options *op, const int def, const char *msg) +{ + DataStruct *d = (DataStruct *) op->ui_priv; + int yes_x, no_x, x, y, w, h, yes, ch; + char *str; + + if (!msg) return FALSE; + + /* check if the window was resized */ + + nv_ncurses_check_resize(d); + + draw_yes_no: + + if (d->message) { + + /* + * XXX we may already have a message region allocated when we + * enter nv_ncurses_yes_no(): we may have been in the middle + * of displaying a progress bar when we encountered an error + * that we need to report. To deal with this situation, throw + * out the existing message region; nv_ncurses_status_update() + * and nv_ncurses_status_end() will have to recreate their + * message region. + */ + + nv_ncurses_destroy_region(d->message); + d->message = NULL; + } + + /* create the message region and print the message in it */ + + nv_ncurses_do_message_region(d, NULL, msg, FALSE, 2); + + /* draw the yes/no buttons */ + + w = 7; + h = 1; + y = d->message->h - 2; + yes_x = (d->message->w - w*3)/2 + 0; + no_x = (d->message->w - w*3)/2 + 2*w; + + yes = def; + + nv_ncurses_draw_button(d, d->message, yes_x, y, w, h, "Yes", yes,FALSE); + nv_ncurses_draw_button(d, d->message, no_x, y, w, h, "No", !yes, FALSE); + + refresh(); + + /* process key strokes */ + + do { + if (nv_ncurses_check_resize(d)) goto draw_yes_no; + ch = getch(); + + switch (ch) { + case NV_NCURSES_TAB: + case KEY_LEFT: + case KEY_RIGHT: + + /* + * any of left, right, and tab will toggle which button is + * selected + */ + + yes ^= 1; + + nv_ncurses_draw_button(d, d->message, yes_x, y, w, h, + "Yes", yes, FALSE); + nv_ncurses_draw_button(d, d->message, no_x, y, w, h, + "No", !yes, FALSE); + refresh(); + break; + default: + break; + } + } while (ch != NV_NCURSES_ENTER); + + /* animate the button being pushed down */ + + x = yes ? yes_x : no_x; + str = yes ? "Yes" : "No"; + + nv_ncurses_erase_button(d->message, x, y, w, h); + nv_ncurses_draw_button(d, d->message, x, y, w, 1, str, TRUE, TRUE); + refresh(); + usleep(NV_NCURSES_BUTTON_PRESS_TIME); + + nv_ncurses_erase_button(d->message, x, y, w, h); + nv_ncurses_draw_button(d, d->message, x, y, w, h, str, TRUE, FALSE); + refresh(); + usleep(NV_NCURSES_BUTTON_PRESS_TIME); + + /* free the message region */ + + nv_ncurses_destroy_region(d->message); + d->message = NULL; + + refresh(); + + return yes; + +} /* nv_ncurses_yes_no() */ + + + + +/* + * nv_ncurses_status_begin() - initialize a progress bar + */ + +static void nv_ncurses_status_begin(Options *op, + const char *title, const char *msg) +{ + DataStruct *d = (DataStruct *) op->ui_priv; + + nv_ncurses_check_resize(d); + + /* cache the progress bar title */ + + d->progress_title = strdup(title); + + /* create the message region for use by the progress bar */ + + nv_ncurses_do_progress_bar_region(d); + + /* write the one line msg (truncating it, if need be) */ + + nv_ncurses_do_progress_bar_message(d, msg, d->message->h - 3, + d->message->w); + + refresh(); + +} /* nv_ncurses_status_begin() */ + + + + +/* + * nv_ncurses_status_update() - update the progress bar + */ + +static void nv_ncurses_status_update(Options *op, const float percent, + const char *msg) +{ + int i, n, h; + int p[4]; + char v[4]; + DataStruct *d = (DataStruct *) op->ui_priv; + + /* + * if the message region was deleted or if the window was resized, + * redraw the entire progress bar region. + */ + + if (nv_ncurses_check_resize(d) || !d->message) { + if (d->message) nv_ncurses_destroy_region(d->message); + nv_ncurses_do_progress_bar_region(d); + } + + /* compute the percentage */ + + n = ((int) (percent * (float) (d->message->w - 2))); + n = NV_MAX(n, 2); + + init_position(p, d->message->w); + init_percentage_string(v, (int) (100.0 * percent)); + + h = d->message->h; + + /* write the one line msg (truncating it, if need be) */ + + nv_ncurses_do_progress_bar_message(d, msg, h - 3, d->message->w - 2); + + /* draw the progress bar */ + + if (d->use_color) { + for (i = 1; i <= n; i++) { + mvaddch(d->message->y + h - 2, d->message->x + i, + choose_char(i, p, v,' ') | + A_REVERSE | NV_NCURSES_INPUT_COLOR); + } + + for (i = 0; i < 4; i++) { + if (p[i] >= (n+1)) { + mvaddch(d->message->y + h - 2, d->message->x + p[i], + (v[i] ? v[i] : ' ') | NV_NCURSES_INPUT_COLOR); + } + } + } else { + for (i = 2; i < n; i++) { + mvaddch(d->message->y + h - 2, d->message->x + i, + choose_char(i, p, v, ' ') | A_REVERSE); + } + + for (i = 0; i < 4; i++) { + if (p[i] >= n) { + mvaddch(d->message->y + h - 2, d->message->x + p[i], + v[i] ? v[i] : '-'); + } + } + } + + refresh(); + +} /* nv_ncurses_status_update() */ + + + + +/* + * nv_ncurses_status_end() - draw the progress bar at 100% + */ + +static void nv_ncurses_status_end(Options *op, const char *msg) +{ + int i, n, h; + int p[4]; + char v[4]; + DataStruct *d = (DataStruct *) op->ui_priv; + + /* + * if the message region was deleted or if the window was resized, + * redraw the entire progress bar region. + */ + + if (nv_ncurses_check_resize(d) || !d->message) { + if (d->message) nv_ncurses_destroy_region(d->message); + nv_ncurses_do_progress_bar_region(d); + } + + n = d->message->w - 2; + + init_position(p, d->message->w); + init_percentage_string(v, 100.0); + + h = d->message->h; + + /* write the one line msg (truncating it, if need be) */ + + nv_ncurses_do_progress_bar_message(d, msg, h - 3, d->message->w - 2); + + /* draw the complete progress bar */ + + if (d->use_color) { + for (i = 1; i < (n+1); i++) { + mvaddch(d->message->y + h - 2, d->message->x + i, + choose_char(i, p, v, ' ') | + A_REVERSE | NV_NCURSES_INPUT_COLOR); + } + } else { + for (i = 2; i < (n); i++) { + mvaddch(d->message->y + h - 2, d->message->x + i, + choose_char(i, p, v, ' ') | A_REVERSE); + } + } + + refresh(); + + free(d->progress_title); + d->progress_title = NULL; + + /* XXX don't free the message window, yet... */ + +} /* nv_ncurses_status_end() */ + + + + +/* + * nv_ncurses_close() - close the ui: free any memory we allocated, + * and end curses mode. + */ + +static void nv_ncurses_close(Options *op) +{ + DataStruct *d = NULL; + + if (op) { + + /* XXX op may be NULL if we get called from a signal handler */ + + d = (DataStruct *) op->ui_priv; + nv_ncurses_destroy_region(d->header); + nv_ncurses_destroy_region(d->footer); + free(d); + } + + clear(); + refresh(); + + endwin(); /* End curses mode */ + + return; + +} /* nv_ncurses_close() */ + + + +/****************************************************************************/ +/* + * internal helper functions for manipulating the header and footer + */ + + + + +/* + * nv_ncurses_set_header() - write the title to the header region; + * note that this function does not call refresh() + */ + +static void nv_ncurses_set_header(DataStruct *d, const char *title) +{ + int x, y; + char *tmp; + + tmp = strdup(title); + + if (d->title) free(d->title); + d->title = tmp; + + x = (d->header->w - strlen(d->title)) / 2; + y = 0; + + attrset(d->header->attr); + + nv_ncurses_clear_region(d->header); + mvaddstr(d->header->y + y, d->header->x + x, (char *) d->title); + attrset(A_NORMAL); + +} /* nv_ncurses_set_header() */ + + + + +/* + * nv_ncurses_set_footer() - write the left and right text to the + * footer; note that this function does not call refresh() + */ + +static void nv_ncurses_set_footer(DataStruct *d, const char *left, + const char *right) +{ + int x, y; + char *tmp0, *tmp1; + + tmp0 = strdup(left); + tmp1 = strdup(right); + + if (d->footer_left) free(d->footer_left); + if (d->footer_right) free(d->footer_right); + + d->footer_left = tmp0; + d->footer_right = tmp1; + + attrset(d->footer->attr); + + nv_ncurses_clear_region(d->footer); + + if (d->footer_left) { + y = 0; + x = 1; + mvaddstr(d->footer->y + y, d->footer->x + x, d->footer_left); + } + if (d->footer_right) { + y = 0; + x = d->footer->w - strlen(d->footer_right) - 1; + mvaddstr(d->footer->y + y, d->footer->x + x, d->footer_right); + } + + attrset(A_NORMAL); + +} /* nv_ncurses_set_footer() */ + + + + +/****************************************************************************/ +/* + * internal helper functions for manipulating RegionStructs + */ + + + + +/* + * nv_ncurses_create_region() - create a new region at the specified + * location with the specified dimensions; note that this function + * does not call refresh() + */ + +static RegionStruct *nv_ncurses_create_region(DataStruct *d, + int x, int y, int w, int h, + int color, + int no_color_attr) +{ + RegionStruct *region = + (RegionStruct *) malloc(sizeof(RegionStruct)); + + region->x = x; + region->y = y; + region->w = w; + region->h = h; + + if (d->use_color) region->attr = color; + else region->attr = no_color_attr; + + /* create a single line for use in clearing the region */ + + region->line = (char *) malloc(w + 1); + memset(region->line, ' ', w); + region->line[w] = '\0'; + + /* clear the region */ + + attrset(region->attr); + nv_ncurses_clear_region(region); + attrset(A_NORMAL); + + return region; + +} /* nv_ncurses_create_region() */ + + + + +/* + * nv_ncurses_clear_region() - clear each line in the region; note + * that this function does not call refresh(), nor does it explicitly + * set any attributes. + */ + +static void nv_ncurses_clear_region(RegionStruct *region) +{ + int i; + + for (i = region->y; i < (region->y + region->h); i++) { + mvaddstr(i, region->x, region->line); + } +} /* nv_ncurses_clear_region() */ + + + + +/* + * nv_ncurses_destroy_region() - clear and free the RegionStruct; note + * that this function does not call refresh() + */ + +static void nv_ncurses_destroy_region(RegionStruct *region) +{ + if (!region) return; + + attrset(A_NORMAL); + nv_ncurses_clear_region(region); + free(region->line); + free(region); + +} /* nv_ncurses_destroy_region() */ + + + + +/****************************************************************************/ +/* + * internal helper functions for drawing buttons + */ + + + + +/* + * nv_ncurses_draw_button() - draw a button on the specified region, + * at location x,y with dimensions w,h. Give the button the label + * str. + * + * The hilite parameter, when TRUE, causes the label to be printed in + * reverse colors. + * + * The down parameter, when TRUE, causes the button to be drawn as if + * it had been pressed down (ie: shifted down and right). + */ + +static void nv_ncurses_draw_button(DataStruct *d, RegionStruct *region, + int x, int y, int w, int h, + char *str, bool hilite, bool down) +{ + int i, j, n, attr = 0; + + if (down) x++, y++; + + n = strlen(str); + n = (n > w) ? 0 : ((w - n) / 2); + + if (d->use_color) { + attr = NV_NCURSES_BUTTON_COLOR; + } else if (hilite) { + attr = A_REVERSE; + } else { + attr = 0; + } + + for (j = y; j < (y + h); j++) { + for (i = x; i < (x + w); i++) { + mvaddch(region->y + j, region->x + i, ' ' | attr); + } + } + + if (hilite) attr |= A_REVERSE; + + attron(attr); + mvaddstr(region->y + y + h/2, region->x + x + n, str); + attroff(attr); + +} /* nv_ncurses_draw_button() */ + + + + +/* + * nv_ncurses_erase_button() - erase the button on the specified + * region at location x,y with dimensions w,h. + */ + +static void nv_ncurses_erase_button(RegionStruct *region, + int x, int y, int w, int h) +{ + int i, j; + + for (j = y; j <= (y + h); j++) { + for (i = x; i <= (x + w); i++) { + mvaddch(region->y + j, region->x + i, ' ' | region->attr); + } + } + +} /* nv_ncurses_erase_button() */ + + + + +/*****************************************************************************/ +/* + * nv_ncurses_do_message_region(), + * nv_ncurses_do_progress_bar_region(), + * nv_ncurses_do_progress_bar_message() - helper functions for drawing + * regions and stuff. + */ + +/* + * nv_ncurses_do_message_region() - create a new message region + * containing the string msg. The "top" argument indicates whether + * the region should be vertically positioned immediately below the + * header, or should be positioned 1/3 of the way down the screen. + * The num_extra_lines argument is used to request extra lines in the + * message region below the string (to leave room for buttons, for + * example). + */ + +static void nv_ncurses_do_message_region(DataStruct *d, const char *prefix, + const char *msg, int top, + int num_extra_lines) +{ + int w, h, x, y; + TextRows *t; + + /* + * compute the width and height that we need (taking into account + * num_extra_lines that the caller may need for buttons + */ + + w = d->width - 2; + t = d->format_text_rows(prefix, msg, w - 2, TRUE); + h = t->n + num_extra_lines + 2; + + /* + * compute the starting position of the message region: either + * immediately below the header or 1/3 of the way down the screen. + */ + + x = 1; + if (top) y = 2; + else y = ((d->height - (3 + h)) / 3) + 1; + + /* create the message region */ + + d->message = nv_ncurses_create_region(d, x, y, w, h, + NV_NCURSES_MESSAGE_COLOR, + NV_NCURSES_MESSAGE_NO_COLOR); + + nv_ncurses_format_print(d, d->message, 1, 1, d->message->w - 2, + d->message->h - (1 + num_extra_lines), t); + + /* free the text rows */ + + nv_ncurses_free_text_rows(t); + +} /* nv_ncurses_do_message_region() */ + + + + +/* + * nv_ncurses_do_progress_bar_region() - create a message region, draw + * the progress bar's title, separator, and the empty progress bar. + */ + +static void nv_ncurses_do_progress_bar_region(DataStruct *d) +{ + int n, h, i, p[4]; + char v[4]; + + /* create the message region and print the title in it */ + + nv_ncurses_do_message_region(d, NULL, d->progress_title, FALSE, 3); + + n = d->message->w - 2; + h = d->message->h; + + attrset(d->message->attr); + + /* draw the horizontal separator */ + + for (i = 1; i <= n; i++) { + mvaddch(d->message->y + h - 4, d->message->x + i, + NV_NCURSES_HLINE | d->message->attr); + } + + /* draw an empty progress bar */ + + init_position(p, n + 2); + init_percentage_string(v, 0); + v[2] = '0'; + + if (d->use_color) { + for (i = 1; i <= n; i++) { + mvaddch(d->message->y + h - 2, d->message->x + i, + choose_char(i, p, v, ' ') | NV_NCURSES_INPUT_COLOR); + } + } else { + mvaddch(d->message->y + h - 2, d->message->x + 1, '['); + for (i = 2; i < n; i++) + mvaddch(d->message->y + h - 2, d->message->x + i, + choose_char(i, p, v, '-')); + mvaddch(d->message->y + h - 2, d->message->x + n, ']'); + } + +} /* nv_ncurses_do_progress_bar_region() */ + + + +/* + * nv_ncurses_do_progress_bar_message() - write the one line progress + * bar message to the message region, truncating the string, if need + * be. + */ + +static void nv_ncurses_do_progress_bar_message(DataStruct *d, const char *str, + int y, int w) +{ + char *tmp; + + /* clear the message line */ + + attrset(d->message->attr); + mvaddstr(d->message->y + y, d->message->x, d->message->line); + + /* write the message string */ + + if (str) { + tmp = malloc(w + 1); + strncpy(tmp, str, w); + tmp[w] = '\0'; + mvaddstr(d->message->y + y, d->message->x + 1, tmp); + free(tmp); + } + +} /* nv_ncurses_do_progress_bar_message() */ + + + + +/***************************************************************************/ +/* + * pager functions + */ + + +/* + * pager functions -- these functions provide the basic behavior of a + * text viewer... used to display TextRows. + * + * d : DataStruct struct + * x : starting x coordinate of the pager + * y : starting y coordinate of the pager + * w : width of the pager + * h : height of the pager + * t : TextRows to be displayed + * label : string to be displayed in the status bar. + */ + +static PagerStruct *nv_ncurses_create_pager(DataStruct *d, + int x, int y, int w, int h, + TextRows *t, const char *label) +{ + PagerStruct *p = (PagerStruct *) malloc(sizeof(PagerStruct)); + + p->t = t; + p->region = nv_ncurses_create_region(d, x, y, w, h, A_NORMAL, A_NORMAL); + p->label = label; + + p->cur = 0; + + p->page = h - 2; + + nv_ncurses_pager_update(d, p); + + return p; + +} /* nv_ncurses_create_pager() */ + + + + +/* + * nv_ncurses_destroy_pager() - free resources associated with the + * pager + */ + +static void nv_ncurses_destroy_pager(PagerStruct *p) +{ + nv_ncurses_destroy_region(p->region); + free(p); + +} /* nv_ncurses_destroy_pager () */ + + + +/* + * nv_ncurses_pager_update() - redraw the text in the pager, and + * update the information about the pager in the footer. Note that + * this function does not call refresh(). + */ + +static void nv_ncurses_pager_update(DataStruct *d, PagerStruct *p) +{ + int i, maxy, percent, denom; + char tmp[10]; + + if (!p) return; + + /* determine the maximum y value for the text */ + + maxy = (p->cur + (p->region->h - 1)); + if (maxy > p->t->n) maxy = p->t->n; + + /* draw the text */ + + attrset(p->region->attr); + + for (i = p->cur; i < maxy; i++) { + mvaddstr(p->region->y + i - p->cur, p->region->x, p->region->line); + if (p->t->t[i]) { + mvaddstr(p->region->y + i - p->cur, p->region->x, p->t->t[i]); + } + } + + /* compute the percentage */ + + denom = p->t->n - (p->region->h - 1); + if (denom < 1) percent = 100; + else percent = ((100.0 * (float) (p->cur)) / (float) denom); + + /* create the percentage string */ + + if (p->t->n <= (p->region->h - 1)) snprintf(tmp, 10, "All"); + else if (percent <= 0) snprintf(tmp, 10, "Top"); + else if (percent >= 100) snprintf(tmp, 10, "Bot"); + else snprintf(tmp, 10, "%3d%%", percent); + + /* update the status in the footer */ + + nv_ncurses_set_footer(d, p->label, tmp); + +} /* nv_ncurses_pager_update() */ + + + +/* + * nv_ncurses_pager_handle_events() - process any keys that affect the + * pager. + */ + +static void nv_ncurses_pager_handle_events(DataStruct *d, + PagerStruct *p, int ch) +{ + int n = p->t->n - (p->region->h - 1); + + if (!p) return; + + switch (ch) { + case KEY_UP: + if (p->cur > 0) { + p->cur--; + nv_ncurses_pager_update(d, p); + refresh(); + } + break; + + case KEY_DOWN: + if (p->cur < n) { + p->cur++; + nv_ncurses_pager_update(d, p); + refresh(); + } + break; + + case KEY_PPAGE: + if (p->cur > 0) { + p->cur -= p->page; + if (p->cur < 0) p->cur = 0; + nv_ncurses_pager_update(d, p); + refresh(); + } + break; + + case KEY_NPAGE: + if (p->cur < n) { + p->cur += p->page; + if (p->cur > n) p->cur = n; + nv_ncurses_pager_update(d, p); + refresh(); + } + break; + } +} /* nv_ncurses_pager_handle_events() */ + + + + + + +/*****************************************************************************/ +/* + * helper functions for the progress bar + */ + +static int choose_char(int i, int p[4], char v[4], char def) +{ + if (p[0] == i) return v[0] ? v[0] : def; + if (p[1] == i) return v[1] ? v[1] : def; + if (p[2] == i) return v[2] ? v[2] : def; + if (p[3] == i) return v[3] ? v[3] : def; + return def; +} + +static void init_percentage_string(char v[4], int n) +{ + int j; + + n = NV_MAX(n, 1); + + v[0] = (n/100); + v[1] = (n/10) - (v[0]*10); + v[2] = (n - (v[0]*100) - (v[1]*10)); + v[0] += '0'; + v[1] += '0'; + v[2] += '0'; + v[3] = '%'; + + for (j = 0; j < 3; j++) { + if (v[j] == '0') v[j] = 0; + else break; + } +} + +static void init_position(int p[4], int w) +{ + p[0] = 0 + (w - 4)/2; + p[1] = 1 + (w - 4)/2; + p[2] = 2 + (w - 4)/2; + p[3] = 3 + (w - 4)/2; +} + + + +/*****************************************************************************/ +/* + * misc helper functions + */ + + +/* + * nv_ncurses_create_command_list_textrows() - build TextRows to + * describe the command list + */ + +static TextRows *nv_ncurses_create_command_list_textrows(DataStruct *d, + CommandList *cl, + int w) +{ + int i, len; + Command *c; + char *str, *perms; + TextRows *t0, *t1; + + t0 = t1 = NULL; + + for (i = 0; i < cl->num; i++) { + c = &cl->cmds[i]; + + str = NULL; + + switch (c->cmd) { + + case INSTALL_CMD: + perms = nv_ncurses_mode_to_permission_string(c->mode); + len = strlen(c->s0) + strlen(c->s1) + strlen(perms) + 64; + str = (char *) malloc(len + 1); + snprintf(str, len, "Install the file '%s' as '%s' with " + "permissions '%s'", c->s0, c->s1, perms); + free(perms); + break; + + case RUN_CMD: + len = strlen(c->s0) + 64; + str = (char *) malloc(len + 1); + snprintf(str, len, "Execute the command `%s`", c->s0); + break; + + case SYMLINK_CMD: + len = strlen(c->s0) + strlen(c->s1) + 64; + str = (char *) malloc(len + 1); + snprintf(str, len, "Create a symbolic link '%s' to '%s'", + c->s0, c->s1); + break; + + case BACKUP_CMD: + len = strlen(c->s0) + 64; + str = (char *) malloc(len + 1); + snprintf(str, len, "Backup the file '%s'", c->s0); + break; + + case DELETE_CMD: + len = strlen(c->s0) + 64; + str = (char *) malloc(len + 1); + snprintf(str, len, "Delete the file '%s'", c->s0); + break; + + default: + /* XXX should not get here */ + break; + } + + if (str) { + t1 = d->format_text_rows(NV_BULLET_STR, str, w, TRUE); + + if (t0) { + nv_ncurses_concat_text_rows(t0, t1); + nv_ncurses_free_text_rows(t1); + } else { + t0 = t1; + } + t1 = NULL; + + free(str); + } + } + + return t0; + +} /* nv_ncurses_create_command_list_textrows() */ + + + +/* + * mode_to_permission_string() - given a mode bitmask, allocate and + * write a permission string. + */ + +static char *nv_ncurses_mode_to_permission_string(mode_t mode) +{ + char *s = (char *) malloc(10); + memset (s, '-', 9); + + if (mode & (1 << 8)) s[0] = 'r'; + if (mode & (1 << 7)) s[1] = 'w'; + if (mode & (1 << 6)) s[2] = 'x'; + + if (mode & (1 << 5)) s[3] = 'r'; + if (mode & (1 << 4)) s[4] = 'w'; + if (mode & (1 << 3)) s[5] = 'x'; + + if (mode & (1 << 2)) s[6] = 'r'; + if (mode & (1 << 1)) s[7] = 'w'; + if (mode & (1 << 0)) s[8] = 'x'; + + s[9] = '\0'; + return s; + +} /* mode_to_permission_string() */ + + +/* + * nv_ncurses_concat_text_rows() - concatenate two text rows + */ + +static void nv_ncurses_concat_text_rows(TextRows *t0, TextRows *t1) +{ + int n, i; + + n = t0->n + t1->n; + + t0->t = (char **) realloc(t0->t, sizeof(char *) * n); + + for (i = 0; i < t1->n; i++) { + t0->t[i + t0->n] = strdup(t1->t[i]); + } + + t0->m = NV_MAX(t0->m, t1->m); + t0->n = n; + +} /* nv_ncurses_concat_text_rows() */ + + + +/* + * nv_free_text_rows() - free the TextRows data structure allocated by + * nv_format_text_rows() + */ + +static void nv_ncurses_free_text_rows(TextRows *t) +{ + int i; + + if (!t) return; + for (i = 0; i < t->n; i++) free(t->t[i]); + if (t->t) free(t->t); + free(t); + +} /* nv_free_text_rows() */ + + + +/* + * nv_ncurses_formatted_printw() - this function formats the string + * str on the region region, wrapping to the next line as necessary, + * using the bounding box specified by x, y, w, and h. + * + * The number of lines printed are returned. + */ + +static int nv_ncurses_format_print(DataStruct *d, RegionStruct *region, + int x, int y, int w, int h, + TextRows *t) +{ + int i, n; + + n = NV_MIN(t->n, h); + + attrset(region->attr); + + for (i = 0; i < n; i++) { + mvaddstr(region->y + y + i, region->x + x, t->t[i]); + } + + attrset(A_NORMAL); + return n; + +} /* nv_ncurses_format_printw() */ + + + +/* + * nv_ncurses_check_resize() - check if the dimensions of the screen + * have changed; if so, update the old header and footer regions, + * clear everything, update our cached dimensions, and recreate the + * header and footer. + * + * XXX we could catch the SIGREGIONCH signal, but that's + * asynchronous... it's safer to only attempt to handle a resize when + * we know we can. + */ + +static int nv_ncurses_check_resize(DataStruct *d) +{ + int x, y; + + getmaxyx(stdscr, y, x); + + if ((x == d->width) && (y == d->height)) { + /* no resize detected... just return */ + return FALSE; + } + + /* we have been resized */ + + /* destroy the old header and footer */ + + nv_ncurses_destroy_region(d->header); + nv_ncurses_destroy_region(d->footer); + + clear(); + + /* update our cached copy of the dimensions */ + + d->height = y; + d->width = x; + + /* recreate the header and footer */ + + d->header = nv_ncurses_create_region(d, 1, 0, d->width - 2, 1, + NV_NCURSES_HEADER_COLOR, + NV_NCURSES_HEADER_NO_COLOR); + + d->footer = nv_ncurses_create_region(d, 1, d->height - 2, d->width - 2, 1, + NV_NCURSES_FOOTER_COLOR, + NV_NCURSES_FOOTER_NO_COLOR); + + nv_ncurses_set_header(d, d->title); + nv_ncurses_set_footer(d, d->footer_left, d->footer_right); + + return TRUE; + +} /* nv_ncurses_check_resize() */ diff --git a/nvidia-installer-ui.h b/nvidia-installer-ui.h new file mode 100644 index 0000000..6e46901 --- /dev/null +++ b/nvidia-installer-ui.h @@ -0,0 +1,155 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * nv_installer_ui.h + */ + +#ifndef __NVIDIA_INSTALLER_UI_H__ +#define __NVIDIA_INSTALLER_UI_H__ + +#include "nvidia-installer.h" +#include "command-list.h" + +#define NV_MSG_LEVEL_LOG 0 +#define NV_MSG_LEVEL_MESSAGE 1 +#define NV_MSG_LEVEL_WARNING 2 +#define NV_MSG_LEVEL_ERROR 3 + +/* + * InstallerrUI - this structure defines the dispatch table that each + * user interface module must provide. + */ + +typedef struct __nv_installer_ui { + + /* + * detect - returns TRUE if the user interface is present (eg for + * gtk check that the DISPLAY environment variable is set and can + * be connected to). + */ + + int (*detect)(Options *op); + + /* + * init - initialize the ui and print a welcome message. + */ + + int (*init)(Options *op, FormatTextRows format_text_rows); + + /* + * set_title - set the title to be displayed by the user interface + */ + + void (*set_title)(Options *op, const char *title); + + /* + * get_input - prompt for user input with the given msg; returns + * the user inputted string. + */ + + char *(*get_input)(Options *op, const char *def, const char *msg); + + /* + * display_license - given the text from the license file, display + * the text to the user and prompt the user for acceptance. + * Returns whether or not the user accepted the license. + */ + + int (*display_license)(Options *op, const char *license); + + /* + * message - display a message at the specified message level; the + * possible message levels are: + * + * NV_MSG_LEVEL_LOG: only print the message to the ui's message + * log + * + * NV_MSG_LEVEL_MESSAGE: display the message where users will see + * it; possibly require them to acknowledge that they've seen it + * (by requiring them to clikc "OK" to continue, for example). + * This message may also be printed in the ui's message log. + * + * NV_MSG_LEVEL_WARNING: display the message where users will see + * it; possibly require them to acknowledge that they've seen it + * (by requiring them to clikc "OK" to continue, for example). + * This message may also be printed in the ui's message log. The + * ui should do something to indicate that this message is a + * warning. + * + * NV_MSG_LEVEL_ERROR: display the message where users will see + * it; possibly require them to acknowledge that they've seen it + * (by requiring them to clikc "OK" to continue, for example). + * This message may also be printed in the ui's message log. The + * ui should do something to indicate that this message is an + * error. + */ + + void (*message)(Options *op, int level, const char *msg); + + /* + * command_output - print output from executing a command; this + * might be stuff like the output from compiling the kernel + * module; a ui might choose to display this in a special window, + * or it might ignore it all together. + */ + + void (*command_output)(Options *op, const char *msg); + + /* + * approve_command_list - display the command list to the user; + * returns TRUE if the user accepts that those commands will be + * executed, or FALSE if the user does not accept. This is only + * done in expert mode. + */ + + int (*approve_command_list)(Options *op, CommandList *c,const char *descr); + + /* + * yes_no - ask the yes/no question 'msg' and return TRUE for yes, + * and FALSE for no. + */ + + int (*yes_no)(Options *op, const int def, const char *msg); + + /* + * status_begin(), status_update(), status_end() - these three + * functions display the status of some process. It is expected + * that status_begin() would be called, followed by some number of + * status_update() calls, followed by a status_end() call. + */ + + void (*status_begin)(Options *op, const char *title, const char *msg); + void (*status_update)(Options *op, const float percent, const char *msg); + void (*status_end)(Options *op, const char *msg); + + /* + * close - close down the ui. + */ + + void (*close)(Options *op); + + +} InstallerUI; + +#endif /* __NVIDIA_INSTALLER_UI_H__ */ diff --git a/nvidia-installer.c b/nvidia-installer.c new file mode 100644 index 0000000..124d526 --- /dev/null +++ b/nvidia-installer.c @@ -0,0 +1,770 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * nv_installer.c + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> +#include <string.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#define _GNU_SOURCE /* XXX not very portable */ +#include <getopt.h> + +#include "nvidia-installer.h" +#include "kernel.h" +#include "user-interface.h" +#include "backup.h" +#include "files.h" +#include "misc.h" +#include "update.h" +#include "format.h" +#include "sanity.h" + +#define TAB " " + +static void print_version(void); +static void print_help(void); +static void print_advanced_options(void); +static void print_help_args_only(int args_only); +static void print_advanced_options_args_only(int args_only); + + + +/* + * print_version() - print the current nvidia-installer version number + */ + +extern const char *pNV_ID; + +static void print_version(void) +{ + fmtout(""); + fmtout(pNV_ID); + fmtoutp(TAB, "The NVIDIA Software Installer for Unix/Linux."); + fmtout(""); + fmtoutp(TAB, "This program is used to install and upgrade " + "The NVIDIA Accelerated Graphics Driver Set for %s-%s.", + INSTALLER_OS, INSTALLER_ARCH); + fmtout(""); + fmtoutp(TAB, "Copyright (C) 2003 NVIDIA Corporation."); + fmtout(""); +} + + + +/* + * print_help() - print usage information + */ + +static void print_help(void) +{ + print_version(); + + fmtout(""); + fmtout("nvidia-installer [options]"); + fmtout(""); + + print_help_args_only(FALSE); + +} /* print_help() */ + + + +static void print_advanced_options(void) +{ + print_version(); + + fmtout(""); + fmtout("nvidia-installer [options]"); + fmtout(""); + + fmtout(""); + fmtout("COMMON OPTIONS:"); + fmtout(""); + + print_help_args_only(FALSE); + + fmtout(""); + fmtout("ADVANCED OPTIONS:"); + fmtout(""); + + print_advanced_options_args_only(FALSE); + +} /* print_advanced_options() */ + + + +static void print_help_args_only(int args_only) +{ + /* + * the args_only parameter is used by makeself.sh to get our + * argument list and description; in this case we don't want to + * format to the width of the terminal, so hardcode the width to + * 65. + */ + + if (args_only) reset_current_terminal_width(65); + + fmtout("-a, --accept-license"); + fmtoutp(TAB, "Bypass the display and prompting for acceptance of the " + "NVIDIA Software License Agreement. By passing this option to " + "nvidia-installer, you indicate that you have read and accept the " + "License Agreement contained in the file 'LICENSE' (in the top " + "level directory of the driver package)."); + fmtout(""); + + fmtout("--update"); + fmtoutp(TAB, "Connect to the NVIDIA ftp server '%s' and determine the " + "latest available driver version. If there is a more recent " + "driver available, automatically download and install it. Any " + "other options given on the commandline will be passed on to the " + "downloaded driver package when installing it.", DEFAULT_FTP_SITE); + fmtout(""); + + fmtout("-v, --version"); + fmtoutp(TAB, "Print the nvidia-installer version and exit."); + fmtout(""); + + fmtout("-h, --help"); + fmtoutp(TAB, "Print usage information for the common commandline options " + "and exit."); + fmtout(""); + + fmtout("-A, --advanced-options"); + fmtoutp(TAB, "Print usage information for the common commandline options " + "as well as the advanced options, and then exit."); + fmtout(""); + +} /* print_help_args_only() */ + + +static void print_advanced_options_args_only(int args_only) +{ + /* + * the args_only parameter is used by makeself.sh to get our + * argument list and description; in this case we don't want to + * format to the width of the terminal, so hardcode the width to + * 65. + */ + + if (args_only) reset_current_terminal_width(65); + + fmtout("-i, --driver-info"); + fmtoutp(TAB, "Print information about the currently installed NVIDIA " + "driver version."); + fmtout(""); + + fmtout("--uninstall"); + fmtoutp(TAB, "Uninstall the currently installed NVIDIA driver."); + fmtout(""); + + fmtout("--sanity"); + fmtoutp(TAB, "Perform basic sanity tests on an existing NVIDIA " + "driver installation."); + fmtout(""); + + fmtout("-e, --expert"); + fmtoutp(TAB, "Enable 'expert' installation mode; more detailed questions " + "will be asked, and more verbose output will be printed; " + "intended for expert users. The questions may be suppressed " + "with the '--no-questions' commandline option."); + fmtout(""); + + fmtout("-q, --no-questions"); + fmtoutp(TAB, "Do not ask any questions; the default (normally 'yes') " + "is assumed for " + "all yes/no questions, and the default string is assumed in " + "any situation where the user is prompted for string input. " + "The one question that is not bypassed by this option is " + "license acceptance; the license may be accepted with the " + "commandline option '--accept-license'."); + fmtout(""); + + fmtout("-s, --silent"); + fmtoutp(TAB, "Run silently; no questions are asked and no output is " + "printed, except for error messages to stderr. This option " + "implies '--ui=none --no-questions --accept-license'."); + fmtout(""); + + fmtout("--x-prefix=[X PREFIX]"); + fmtoutp(TAB, "The prefix under which the X components of the " + "NVIDIA driver will be installed; the default is: '%s'. Only " + "under rare circumstances should this option be used.", + DEFAULT_XFREE86_INSTALLATION_PREFIX); + fmtout(""); + + fmtout("--opengl-prefix=[OPENGL PREFIX]"); + fmtoutp(TAB, "The prefix under which the OpenGL components of the " + "NVIDIA driver will be installed; the default is: '%s'. Only ", + "under rare circumstances should this option be used. The Linux " + "OpenGL ABI (http://oss.sgi.com/projects/ogl-sample/ABI/) " + "mandates this default value.", + DEFAULT_OPENGL_INSTALLATION_PREFIX); + fmtout(""); + + fmtout("--installer-prefix=[INSTALLER PREFIX]"); + fmtoutp(TAB, "The prefix under which the installer binary will be " + "installed; the default is: '%s'. Note: use the " + "\"--utility-prefix\" option instead.", + DEFAULT_INSTALLER_INSTALLATION_PREFIX); + fmtout(""); + + fmtout("--utility-prefix=[UTILITY PREFIX]"); + fmtoutp(TAB, "The prefix under which the various NVIDIA utilities " + "(nvidia-installer, nvidia-settings, nvidia-bug-report.sh) will " + "be installed; the default is: '%s'.", + DEFAULT_UTILITY_INSTALLATION_PREFIX); + fmtout(""); + + fmtout("--kernel-include-path=[KERNEL INCLUDE PATH]"); + fmtoutp(TAB, "The directory containing the kernel include files that " + "should be used when compiling the NVIDIA kernel module. " + "This option is deprecated; please use '--kernel-source-path' " + "instead."); + fmtout(""); + + fmtout("--kernel-source-path=[KERNEL SOURCE PATH]"); + fmtoutp(TAB, "The directory containing the kernel source files that " + "should be used when compiling the NVIDIA kernel module. " + "When not specified, the installer will use " + "'/lib/modules/`uname -r`/build', if that " + "directory exists. Otherwise, it will use " + "'/usr/src/linux'."); + fmtout(""); + + fmtout("--kernel-install-path=[KERNEL INSTALL PATH]"); + fmtoutp(TAB, "The directory in which the NVIDIA kernel module should be " + "installed. The default value is either '/lib/modules/`uname " + "-r`/kernel/drivers/video' (if '/lib/modules/`uname -r`/kernel' " + "exists) or '/lib/modules/`uname -r`/video'."); + fmtout(""); + + fmtout("--proc-mount-point=[PROC FILESYSTEM MOUNT POINT]"); + fmtoutp(TAB, "The mount point for the proc file system; if not " + "specified, then this value defaults to '%s' (which is normally " + "correct). The mount point of the proc filesystem is needed " + "because the contents of '<proc filesystem>/version' is used when " + "identifying if a precompiled kernel interface is available for " + "the currently running kernel. This option should only be needed " + "in very rare circumstances.", DEFAULT_PROC_MOUNT_POINT); + fmtout(""); + + fmtout("--log-file-name=[LOG FILE NAME]"); + fmtoutp(TAB, "File name of the installation log file (the default is: " + "'%s').", DEFAULT_LOG_FILE_NAME); + fmtout(""); + + fmtout("--tmpdir=[TMPDIR]"); + fmtoutp(TAB, "Use the specified directory as a temporary directory when " + "downloading files from the NVIDIA ftp site; " + "if not given, then the following list will be searched, and " + "the first one that exists will be used: $TMPDIR, /tmp, ., " + "$HOME."); + fmtout(""); + + fmtout("-m, --ftp-mirror=[FTP MIRROR]"); + fmtoutp(TAB, "Use the specified ftp mirror rather than the default '%s' " + "when downloading driver updates.", DEFAULT_FTP_SITE); + fmtout(""); + + fmtout("-l, --latest"); + fmtoutp(TAB, "Connect to the NVIDIA ftp server %s (or use the ftp mirror " + "specified with the '--ftp-mirror' option) and query the most " + "recent %s-%s driver version number.", DEFAULT_FTP_SITE, + INSTALLER_OS, INSTALLER_ARCH); + fmtout(""); + + fmtout("-f, --force-update"); + fmtoutp(TAB, "Forces an update to proceed, even if the installer " + "thinks the latest driver is already installed; this option " + "implies '--update'."); + fmtout(""); + + fmtout("--ui=[USER INTERFACE]"); + fmtoutp(TAB, "Specify what user interface to use, if available. " + "Valid values are 'ncurses' (the default) or 'none'. " + "If the ncurses interface fails to initialize, or 'none' " + "is specified, then a simple printf/scanf interface will " + "be used."); + fmtout(""); + + fmtout("-c, --no-ncurses-color"); + fmtoutp(TAB, "Disable use of color in the ncurses user interface."); + fmtout(""); + + fmtout("--opengl-headers"); + fmtoutp(TAB, "Normally, installation does not install NVIDIA's OpenGL " + "header files. This option enables installation of the NVIDIA " + "OpenGL header files."); + fmtout(""); + + fmtout("--force-tls=[TLS TYPE]"); + fmtoutp(TAB, "NVIDIA's OpenGL libraries are compiled with one of two " + "different thread local storage (TLS) mechanisms: 'classic tls' " + "which is used on systems with glibc 2.2 or older, and 'new tls' " + "which is used on systems with tls-enabled glibc 2.3 or newer. " + "The nvidia-installer will select the OpenGL " + "libraries appropriate for your system; however, you may use " + "this option to force the installer to install one library " + "type or another. Valid values for [TLS TYPE] are 'new' and " + "'classic'."); + fmtout(""); + + fmtout("-k, --kernel-name=[KERNELNAME]"); + fmtoutp(TAB, "Build and install the NVIDIA kernel module for the " + "non-running kernel specified by [KERNELNAME] ([KERNELNAME] " + "should be the output of `uname -r` when the target kernel is " + "actually running). This option implies " + "'--no-precompiled-interface'. If the options " + "'--kernel-install-path' and '--kernel-source-path' are not " + "given, then they will be inferred from [KERNELNAME]; eg: " + "'/lib/modules/[KERNELNAME]/kernel/drivers/video/' and " + "'/lib/modules/[KERNELNAME]/build/', respectively."); + fmtout(""); + + fmtout("-n, --no-precompiled-interface"); + fmtoutp(TAB, "Disable use of precompiled kernel interfaces."); + fmtout(""); + + fmtout("--no-runlevel-check"); + fmtoutp(TAB, "Normally, the installer checks the current runlevel and " + "warns users if they are in runlevel 1: in runlevel 1, some " + "services that are normally active are disabled (such as devfs), " + "making it difficult for the installer to properly setup the " + "kernel module configuration files. This option disables the " + "runlevel check."); + fmtout(""); + + fmtout("--no-abi-note"); + fmtoutp(TAB, "The NVIDIA OpenGL libraries contain an OS ABI note tag, " + "which identifies the minimum kernel version needed to use the " + "library. This option causes the installer to remove this note " + "from the OpenGL libraries during installation."); + fmtout(""); + + fmtout("--no-rpms"); + fmtoutp(TAB, "Normally, the installer will check for several rpms that " + "conflict with the driver (specifically: NVIDIA_GLX and " + "NVIDIA_kernel), and remove them if present. This option " + "disables this check."); + fmtout(""); + + fmtout("-b, --no-backup"); + fmtoutp(TAB, "During driver installation, conflicting files are backed " + "up, so that they can be restored when the driver is " + "uninstalled. This option causes the installer to simply delete " + "conflicting files, rather than back them up."); + fmtout(""); + + fmtout("--no-network"); + fmtoutp(TAB, "This option instructs the installer to not attempt to " + "connect to the NVIDIA ftp site (for updated precompiled kernel " + "interfaces, for example)."); + fmtout(""); + + fmtout("-K, --kernel-module-only"); + fmtoutp(TAB, "Install a kernel module only, and don't uninstall the " + "existing driver. This is intended to be used to install kernel " + "modules for additional kernels (in cases where you might boot " + "between several different kernels). To use this option, you " + "must already have a driver installed, and the version of the " + "installed driver must match the version of this kernel " + "module."); + fmtout(""); + + fmtout("--precompiled-kernel-interfaces-path"); + fmtoutp(TAB, "Before searching for a precompiled kernel interface in the " + ".run file, search in the specified directory."); + fmtout(""); + +} /* print_advanced_options_args_only() */ + + +/* + * parse_commandline() - malloc an Options structure, initialize it, + * and fill in any pertinent data from the commandline arguments; it + * is intended that this function do only minimal sanity checking -- + * just enough error trapping to ensure correct syntax of the + * commandline options. Validation of the actual data specified + * through the options is left for the functions that use this data. + * + * XXX Would it be better to do more validation now? + * + * XXX this implementation uses getopt_long(), which isn't portable to + * non-glibc based systems... + */ + +Options *parse_commandline(int argc, char *argv[]) +{ + Options *op; + int c, option_index = 0; + +#define XFREE86_PREFIX_OPTION 1 +#define OPENGL_PREFIX_OPTION 2 +#define KERNEL_INCLUDE_PATH_OPTION 3 +#define KERNEL_INSTALL_PATH_OPTION 4 +#define UNINSTALL_OPTION 5 +#define PROC_MOUNT_POINT_OPTION 6 +#define USER_INTERFACE_OPTION 7 +#define LOG_FILE_NAME_OPTION 8 +#define HELP_ARGS_ONLY_OPTION 9 +#define TMPDIR_OPTION 10 +#define OPENGL_HEADERS_OPTION 11 +#define INSTALLER_PREFIX_OPTION 12 +#define FORCE_TLS_OPTION 13 +#define SANITY_OPTION 14 +#define ADVANCED_OPTIONS_ARGS_ONLY_OPTION 15 +#define UTILITY_PREFIX_OPTION 16 +#define ADD_THIS_KERNEL_OPTION 17 +#define RPM_FILE_LIST_OPTION 18 +#define NO_RUNLEVEL_CHECK_OPTION 19 +#define NO_NETWORK_OPTION 20 +#define PRECOMPILED_KERNEL_INTERFACES_PATH 21 +#define NO_ABI_NOTE_OPTION 22 +#define KERNEL_SOURCE_PATH_OPTION 23 +#define NO_RPMS_OPTION 24 +#define X_PREFIX_OPTION 25 + + + static struct option long_options[] = { + { "accept-license", 0, NULL, 'a' }, + { "update", 0, NULL, 'u' }, + { "force-update", 0, NULL, 'f' }, + { "expert", 0, NULL, 'e' }, + { "version", 0, NULL, 'v' }, + { "debug", 0, NULL, 'd' }, + { "driver-info", 0, NULL, 'i' }, + { "no-precompiled-interface", 0, NULL, 'n' }, + { "no-ncurses-color", 0, NULL, 'c' }, + { "latest", 0, NULL, 'l' }, + { "ftp-mirror", 1, NULL, 'm' }, + { "no-questions", 0, NULL, 'q' }, + { "kernel-name", 1, NULL, 'k' }, + { "silent", 0, NULL, 's' }, + { "help", 0, NULL, 'h' }, + { "advanced-options", 0, NULL, 'A' }, + { "no-backup", 0, NULL, 'b' }, + { "kernel-module-only", 0, NULL, 'K' }, + { "xfree86-prefix", 1, NULL, XFREE86_PREFIX_OPTION }, + { "x-prefix", 1, NULL, X_PREFIX_OPTION }, + { "opengl-prefix", 1, NULL, OPENGL_PREFIX_OPTION }, + { "installer-prefix", 1, NULL, INSTALLER_PREFIX_OPTION }, + { "utility-prefix", 1, NULL, UTILITY_PREFIX_OPTION }, + { "kernel-include-path", 1, NULL, KERNEL_INCLUDE_PATH_OPTION }, + { "kernel-source-path", 1, NULL, KERNEL_SOURCE_PATH_OPTION }, + { "kernel-install-path", 1, NULL, KERNEL_INSTALL_PATH_OPTION }, + { "uninstall", 0, NULL, UNINSTALL_OPTION }, + { "proc-mount-point", 1, NULL, PROC_MOUNT_POINT_OPTION }, + { "ui", 1, NULL, USER_INTERFACE_OPTION }, + { "log-file-name", 1, NULL, LOG_FILE_NAME_OPTION }, + { "help-args-only", 0, NULL, HELP_ARGS_ONLY_OPTION }, + { "tmpdir", 1, NULL, TMPDIR_OPTION }, + { "opengl-headers", 0, NULL, OPENGL_HEADERS_OPTION }, + { "force-tls", 1, NULL, FORCE_TLS_OPTION }, + { "sanity", 0, NULL, SANITY_OPTION }, + { "add-this-kernel", 0, NULL, ADD_THIS_KERNEL_OPTION }, + { "rpm-file-list", 1, NULL, RPM_FILE_LIST_OPTION }, + { "no-runlevel-check", 0, NULL, NO_RUNLEVEL_CHECK_OPTION }, + { "no-network", 0, NULL, NO_NETWORK_OPTION }, + { "no-abi-note", 0, NULL, NO_ABI_NOTE_OPTION }, + { "no-rpms", 0, NULL, NO_RPMS_OPTION }, + { "precompiled-kernel-interfaces-path", 1, NULL, + PRECOMPILED_KERNEL_INTERFACES_PATH }, + { "advanced-options-args-only", 0, NULL, + ADVANCED_OPTIONS_ARGS_ONLY_OPTION }, + { 0, 0, NULL, 0 } + }; + + op = (Options *) nvalloc(sizeof(Options)); + + /* statically initialized strings */ + + op->xfree86_prefix = DEFAULT_XFREE86_INSTALLATION_PREFIX; + op->opengl_prefix = DEFAULT_OPENGL_INSTALLATION_PREFIX; + op->installer_prefix = NULL; + op->utility_prefix = DEFAULT_UTILITY_INSTALLATION_PREFIX; + op->proc_mount_point = DEFAULT_PROC_MOUNT_POINT; + op->log_file_name = DEFAULT_LOG_FILE_NAME; + op->ftp_site = DEFAULT_FTP_SITE; + op->tmpdir = get_tmpdir(op); + op->no_runlevel_check = FALSE; + op->no_backup = FALSE; + op->no_network = FALSE; + op->kernel_module_only = FALSE; + op->no_abi_note = FALSE; + + op->logging = TRUE; /* log by default */ + + while (1) { + + c = getopt_long(argc, argv, "afg:evdinclm:qk:shAbK", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + + case 'a': op->accept_license = TRUE; break; + case 'u': op->update = TRUE; break; + case 'e': op->expert = TRUE; break; + case 'v': print_version(); exit(0); break; + case 'd': op->debug = TRUE; break; + case 'i': + op->driver_info = TRUE; + op->ui_str = "none"; + break; + + case 'n': op->no_precompiled_interface = TRUE; break; + case 'c': op->no_ncurses_color = TRUE; break; + case 'l': op->latest = TRUE; break; + case 'm': op->ftp_site = optarg; break; + case 'f': op->update = op->force_update = TRUE; break; + case 'h': print_help(); exit(0); break; + case 'A': print_advanced_options(); exit(0); break; + case 'q': op->no_questions = TRUE; break; + case 'b': op->no_backup = TRUE; break; + case 'K': op->kernel_module_only = TRUE; break; + case 's': + op->silent = op->no_questions = op->accept_license = TRUE; + op->ui_str = "none"; + break; + + case 'k': + op->kernel_name = optarg; + op->no_precompiled_interface = TRUE; + break; + + case XFREE86_PREFIX_OPTION: + case X_PREFIX_OPTION: + op->xfree86_prefix = optarg; break; + case OPENGL_PREFIX_OPTION: + op->opengl_prefix = optarg; break; + case INSTALLER_PREFIX_OPTION: + op->installer_prefix = optarg; break; + case UTILITY_PREFIX_OPTION: + op->utility_prefix = optarg; break; + case KERNEL_SOURCE_PATH_OPTION: + op->kernel_source_path = optarg; break; + case KERNEL_INCLUDE_PATH_OPTION: + op->kernel_include_path = optarg; break; + case KERNEL_INSTALL_PATH_OPTION: + op->kernel_module_installation_path = optarg; break; + case UNINSTALL_OPTION: + op->uninstall = TRUE; break; + case PROC_MOUNT_POINT_OPTION: + op->proc_mount_point = optarg; break; + case USER_INTERFACE_OPTION: + op->ui_str = optarg; break; + case LOG_FILE_NAME_OPTION: + op->log_file_name = optarg; break; + case HELP_ARGS_ONLY_OPTION: + print_help_args_only(TRUE); exit(0); break; + case TMPDIR_OPTION: + op->tmpdir = optarg; break; + case OPENGL_HEADERS_OPTION: + op->opengl_headers = TRUE; break; + case FORCE_TLS_OPTION: + if (strcasecmp(optarg, "new") == 0) + op->which_tls = FORCE_NEW_TLS; + else if (strcasecmp(optarg, "classic") == 0) + op->which_tls = FORCE_CLASSIC_TLS; + else { + fmterr(""); + fmterr("Invalid parameter for '--force-tls'; please " + "run `%s --help` for usage information.", argv[0]); + fmterr(""); + exit(1); + } + break; + case SANITY_OPTION: + op->sanity = TRUE; + break; + case ADD_THIS_KERNEL_OPTION: + op->add_this_kernel = TRUE; + break; + case ADVANCED_OPTIONS_ARGS_ONLY_OPTION: + print_advanced_options_args_only(TRUE); exit(0); + break; + case RPM_FILE_LIST_OPTION: + op->rpm_file_list = optarg; + break; + case NO_RUNLEVEL_CHECK_OPTION: + op->no_runlevel_check = TRUE; + break; + case NO_NETWORK_OPTION: + op->no_network = TRUE; + break; + case PRECOMPILED_KERNEL_INTERFACES_PATH: + op->precompiled_kernel_interfaces_path = optarg; + break; + case NO_ABI_NOTE_OPTION: + op->no_abi_note = TRUE; + break; + case NO_RPMS_OPTION: + op->no_rpms = TRUE; + break; + + default: + fmterr(""); + fmterr("Invalid commandline, please run `%s --help` " + "for usage information.", argv[0]); + fmterr(""); + exit(1); + } + + op->update_arguments = append_update_arguments(op->update_arguments, + c, optarg, + long_options); + } + + if (optind < argc) { + fmterr(""); + fmterr("Unrecognized arguments:"); + while (optind < argc) + fmterrp(" ", argv[optind++]); + fmterr("Invalid commandline, please run `%s --help` for " + "usage information.", argv[0]); + fmterr(""); + exit(1); + } + + op->distro = get_distribution(op); + + /* + * if the installer prefix was not specified, default it to the + * utility prefix; this is done so that the installer prefix is + * preferred, but if not specified, we default to what was + * specified for the utility prefix. + */ + + if (!op->installer_prefix) { + op->installer_prefix = op->utility_prefix; + } + + return (op); + +} /* parse_commandline() */ + + + +/* + * main program entry point + */ + +int main(int argc, char *argv[]) +{ + Options *op; + int ret = FALSE; + + /* parse the commandline options */ + + op = parse_commandline(argc, argv); + + /* init the log file */ + + log_init(op); + + /* chdir() to the directory containing the binary */ + + if (!adjust_cwd(op, argv[0])) return 1; + + /* initialize the user interface */ + + if (!ui_init(op)) return 1; + + /* check that we're running as root */ + + if (!check_euid(op)) goto done; + + /* check that we're in a safe runlevel */ + + if (!check_runlevel(op)) goto done; + + /* + * find the system utilities we'll need + * + * XXX we won't always need all of these... should only look for + * the ones we need. + */ + + if (!find_system_utils(op)) goto done; + + /* get the latest available driver version */ + + if (op->latest) { + ret = report_latest_driver_version(op); + } + + /* get driver information */ + + else if (op->driver_info) { + ret = report_driver_information(op); + } + + /* perform sanity tests */ + + else if (op->sanity) { + ret = sanity(op); + } + + /* uninstall */ + + else if (op->uninstall) { + ret = uninstall_existing_driver(op, TRUE); + } + + /* update */ + + else if (op->update) { + ret = update(op); + } + + /* add this kernel */ + + else if (op->add_this_kernel) { + ret = add_this_kernel(op); + } + + /* install from the cwd */ + + else { + ret = install_from_cwd(op); + } + + done: + + ui_close(op); + + return (ret ? 0 : 1); + +} /* main() */ diff --git a/nvidia-installer.h b/nvidia-installer.h new file mode 100644 index 0000000..92f8257 --- /dev/null +++ b/nvidia-installer.h @@ -0,0 +1,321 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * nv_installer.h + */ + +#ifndef __NVIDIA_INSTALLER_H__ +#define __NVIDIA_INSTALLER_H__ + +#include <sys/types.h> + + +/* + * Enumerated type, listing each of the system utilities we'll need. + * Keep this enum in sync with the needed_utils string array in + * misc.c:find_system_utils() + */ + +typedef enum { + INSMOD = 0, + MODPROBE, + RMMOD, + LSMOD, + DEPMOD, + LDCONFIG, + LD, + OBJCOPY, + MAX_UTILS +} Utils; + + +/* + * Enumerated type of distributions; this isn't an exhaustive list of + * supported distributions... just distributions that have asked for + * special behavior. + */ + +typedef enum { + SUSE, + UNITED_LINUX, + OTHER +} Distribution; + + +typedef unsigned int uint32; +typedef unsigned char uint8; + + + +/* + * Options structure; malloced by and initialized by + * parse_commandline() and used by all the other functions in the + * installer. + */ + + +typedef struct __options { + + int accept_license; + int update; + int expert; + int uninstall; + int driver_info; + int debug; + int logging; + int no_precompiled_interface; + int no_ncurses_color; + int latest; + int force_update; + int opengl_headers; + int no_questions; + int silent; + int which_tls; + int sanity; + int add_this_kernel; + int no_runlevel_check; + int no_backup; + int no_network; + int kernel_module_only; + int no_abi_note; + int no_rpms; + + char *xfree86_prefix; + char *opengl_prefix; + char *installer_prefix; + char *utility_prefix; + + char *kernel_source_path; + char *kernel_include_path; + char *kernel_module_installation_path; + char *utils[MAX_UTILS]; + + char *proc_mount_point; + char *ui_str; + char *log_file_name; + + char *ftp_site; + + char *tmpdir; + char *update_arguments; + char *kernel_name; + char *rpm_file_list; + char *precompiled_kernel_interfaces_path; + + Distribution distro; + + void *ui_priv; /* for use by the ui's */ + +} Options; + + +typedef struct __package_entry { + + char *file; /* + * filename in the package, relative to cwd of the + * .manifest file. + */ + + char *path; /* + * + */ + + + char *name; /* + * filename without any leading directory + * components; this is just a pointer into the + * 'file' field. + */ + + char *target; /* + * For Package entries that are symbolic links, + * the target indicates the target of the link; + * NULL for non-symbolic link Package entries. + */ + + char *dst; /* + * The fully-qualified filename of the destination + * location of the Package entry; NULL for entries + * that are not installed on the filesystem. This + * field is assigned by the set_destinations() + * function. + */ + + unsigned int flags; + mode_t mode; + +} PackageEntry; + + +typedef struct __package { + + int major, minor, patch; + + char *description; + char *version_string; + char *kernel_module_filename; + char *kernel_interface_filename; + char *kernel_module_name; + char **bad_modules; + char **bad_module_filenames; + char *kernel_module_build_directory; + char *precompiled_kernel_interface_directory; + + PackageEntry *entries; /* array of filename/checksum/bytesize entries */ + int num_entries; + +} Package; + + + +typedef struct { + char **t; /* the text rows */ + int n; /* number of rows */ + int m; /* maximum row length */ +} TextRows; + + + + +/* define boolean values TRUE and FALSE */ + +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ + +#ifndef FALSE +#define FALSE 0 +#endif /* FALSE */ + + +/* flags for passing into install_from_cwd() */ + +#define ADJUST_CWD 0x01 + + +/* default line length for strings */ + +#define NV_LINE_LEN 1024 +#define NV_MIN_LINE_LEN 256 + +/* file types */ + +#define FILE_TYPE_MASK 0x0000ffff + +#define FILE_TYPE_KERNEL_MODULE_SRC 0x00000001 +#define FILE_TYPE_KERNEL_MODULE_CMD 0x00000002 +#define FILE_TYPE_OPENGL_HEADER 0x00000004 +#define FILE_TYPE_OPENGL_LIB 0x00000008 +#define FILE_TYPE_XFREE86_LIB 0x00000010 +#define FILE_TYPE_DOCUMENTATION 0x00000020 +#define FILE_TYPE_OPENGL_SYMLINK 0x00000040 +#define FILE_TYPE_XFREE86_SYMLINK 0x00000080 +#define FILE_TYPE_KERNEL_MODULE 0x00000100 +#define FILE_TYPE_INSTALLER_BINARY 0x00000200 +#define FILE_TYPE_UTILITY_BINARY 0x00000400 +#define FILE_TYPE_LIBGL_LA 0x00000800 + +/* file class: this is used to distinguish OpenGL libraries */ + +#define FILE_CLASS_MASK 0xffff0000 + +#define FILE_CLASS_NEW_TLS 0x00010000 +#define FILE_CLASS_CLASSIC_TLS 0x00020000 +#define FILE_CLASS_NEW_TLS_32 0x00040000 + + +#define FILE_TYPE_INSTALLABLE_FILE (FILE_TYPE_OPENGL_LIB | \ + FILE_TYPE_XFREE86_LIB | \ + FILE_TYPE_DOCUMENTATION | \ + FILE_TYPE_OPENGL_HEADER | \ + FILE_TYPE_KERNEL_MODULE | \ + FILE_TYPE_INSTALLER_BINARY | \ + FILE_TYPE_UTILITY_BINARY) + +#define FILE_TYPE_HAVE_PATH (FILE_TYPE_OPENGL_LIB | \ + FILE_TYPE_OPENGL_SYMLINK | \ + FILE_TYPE_LIBGL_LA | \ + FILE_TYPE_XFREE86_LIB | \ + FILE_TYPE_XFREE86_SYMLINK | \ + FILE_TYPE_DOCUMENTATION) + +#define FILE_TYPE_SYMLINK (FILE_TYPE_OPENGL_SYMLINK | \ + FILE_TYPE_XFREE86_SYMLINK) + + +#define SELECT_TLS 0 +#define FORCE_CLASSIC_TLS 1 +#define FORCE_NEW_TLS 2 + +#define PERM_MASK (S_IRWXU|S_IRWXG|S_IRWXO) + +#define PRECOMPILED_KERNEL_INTERFACE_FILENAME "precompiled-nv-linux.o" + +#define DEFAULT_XFREE86_INSTALLATION_PREFIX "/usr/X11R6" +#define DEFAULT_OPENGL_INSTALLATION_PREFIX "/usr" +#define DEFAULT_INSTALLER_INSTALLATION_PREFIX "/usr" +#define DEFAULT_UTILITY_INSTALLATION_PREFIX "/usr" + + +#define DEFAULT_PROC_MOUNT_POINT "/proc" + +#define DEFAULT_FTP_SITE "ftp://download.nvidia.com" + +#define OPENGL_HEADER_DST_PATH "include/GL" +#define INSTALLER_BINARY_DST_PATH "bin" +#define UTILITY_BINARY_DST_PATH "bin" + +#define LICENSE_FILE "LICENSE" + +#define DEFAULT_LOG_FILE_NAME "/var/log/nvidia-installer.log" + +#define NUM_TIMES_QUESTIONS_ASKED 3 + +#define LD_OPTIONS "-d -r" +#define NVIDIA_VERSION_PROC_FILE "/proc/driver/nvidia/version" + +#define NV_BULLET_STR "-> " +#define NV_CMD_OUT_PREFIX " " + +/* useful macros */ + +#define NV_MIN(x,y) ((x) < (y) ? (x) : (y)) +#define NV_MAX(x,y) ((x) > (y) ? (x) : (y)) + + +/* prototypes of functions used throughout the installer */ + +void log_init(Options *op); +void log_printf(Options *op, const int wb, + const char *prefix, const char *fmt, ...); + +int install_from_cwd(Options *op); +int add_this_kernel(Options *op); + +/* XXX */ + +typedef TextRows *(*FormatTextRows)(const char*, const char*, int, int); + + +#endif /* __NVIDIA_INSTALLER_H__ */ diff --git a/precompiled.c b/precompiled.c new file mode 100644 index 0000000..1a5ef46 --- /dev/null +++ b/precompiled.c @@ -0,0 +1,341 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * precompiled.c - this source file contains functions for dealing + * with precompiled kernel interfaces. + * + * XXX portions of these functions are lifted from mkprecompiled (it + * was much easier to duplicate them than to finesse the code to be + * shared between the installer and mkprecompiled). + */ + + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <errno.h> +#include <stdlib.h> + +#include "nvidia-installer.h" +#include "user-interface.h" +#include "precompiled.h" +#include "misc.h" +#include "crc.h" + +#define PRECOMPILED_CONSTANT_LENGTH (8 + 4 + 12 + 4 + 4) + +/* + * decode_uint32() - given an index into a buffer, read the next 4 + * bytes, and build a uint32. + */ + +static uint32 decode_uint32(uint8 *buf) +{ + uint32 ret = 0; + + ret += (uint32) buf[3]; + ret <<= 8; + + ret += (uint32) buf[2]; + ret <<= 8; + + ret += (uint32) buf[1]; + ret <<= 8; + + ret += (uint32) buf[0]; + ret <<= 0; + + return ret; + +} /* decode_uint32() */ + + + +/* + * read_proc_version() - return the string contained in /proc/version + * + * XXX could use getmntent() to determine where the proc filesystem is + * mounted. + */ + +char *read_proc_version(Options *op) +{ + int fd, ret, len, version_len; + char *version, *c = NULL; + char *proc_verson_filename; + + len = strlen(op->proc_mount_point) + 9; /* strlen("/version") + 1 */ + proc_verson_filename = (char *) nvalloc(len); + snprintf(proc_verson_filename, len, "%s/version", op->proc_mount_point); + + if ((fd = open(proc_verson_filename, O_RDONLY)) == -1) { + ui_warn(op, "Unable to open the file '%s' (%s).", + proc_verson_filename, strerror(errno)); + return NULL; + } + + /* + * it would be more convenient if we could just mmap(2) + * /proc/version, but proc files do not support mmap(2), so read + * the file instead + */ + + len = version_len = 0; + version = NULL; + + while (1) { + if (version_len == len) { + version_len += NV_LINE_LEN; + version = nvrealloc(version, version_len); + c = version + len; + } + ret = read(fd, c, version_len - len); + if (ret == -1) { + ui_warn(op, "Error reading %s (%s).", + proc_verson_filename, strerror(errno)); + free(version); + return NULL; + } + if (ret == 0) { + *c = '\0'; + break; + } + len += ret; + c += ret; + } + + /* replace a newline with a null-terminator */ + + c = version; + while ((*c != '\0') && (*c != '\n')) c++; + *c = '\0'; + + free(proc_verson_filename); + + return version; + +} /* read_proc_version() */ + + + +/* + * precompiled_unpack() - unpack the specified package. It's not + * really an error if we can't open the file or if it's not the right + * format, so just throw an expert-only log message. + */ + +PrecompiledInfo *precompiled_unpack(Options *op, + const char *filename, + const char *output_filename, + const char *real_proc_version_string, + const int package_major, + const int package_minor, + const int package_patch) +{ + int dst_fd, fd, offset, len = 0; + uint8 *buf, *dst; + uint32 crc, major, minor, patch, val, size; + char *description, *proc_version_string; + struct stat stat_buf; + PrecompiledInfo *info = NULL; + + fd = dst_fd = size = 0; + buf = dst = description = proc_version_string = NULL; + + /* open the file to be unpacked */ + + if ((fd = open(filename, O_RDONLY)) == -1) { + ui_expert(op, "Unable to open precompiled kernel interface file " + "'%s' (%s)", filename, strerror(errno)); + goto done; + } + + /* get the file length */ + + if (fstat(fd, &stat_buf) == -1) { + ui_expert(op, "Unable to determine '%s' file length (%s).", + filename, strerror(errno)); + goto done; + } + size = stat_buf.st_size; + + /* check for a minimum length */ + + if (size < PRECOMPILED_CONSTANT_LENGTH) { + ui_expert(op, "File '%s' appears to be too short.", filename); + goto done; + } + + /* mmap(2) the input file */ + + buf = mmap(0, size, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0); + if (buf == (void *) -1) { + ui_expert(op, "Unable to mmap file %s (%s).", + filename, strerror(errno)); + goto done; + } + offset = 0; + + /* check for the header */ + + if (strncmp(buf + offset, "NVIDIA ", 8) != 0) { + ui_expert(op, "File '%s': unrecognized file format.", filename); + goto done; + } + offset += 8; + + /* read the crc */ + + crc = decode_uint32(buf + offset); + offset += 4; + + /* read the version */ + + major = decode_uint32(buf + offset + 0); + minor = decode_uint32(buf + offset + 4); + patch = decode_uint32(buf + offset + 8); + offset += 12; + + /* check if this precompiled kernel interface is the right driver + version */ + + if ((major != package_major) || + (minor != package_minor) || + (patch != package_patch)) { + goto done; + } + + + /* read the description */ + + val = decode_uint32(buf + offset); + offset += 4; + if ((val + PRECOMPILED_CONSTANT_LENGTH) > size) { + ui_expert(op, "Invalid file '%s' (bad description string length %d).", + filename, val); + goto done; + } + if (val > 0) { + description = nvalloc(val+1); + memcpy(description, buf + offset, val); + description[val] = '\0'; + } else { + description = NULL; + } + offset += val; + + /* read the proc version string */ + + val = decode_uint32(buf + offset); + offset += 4; + if ((val + PRECOMPILED_CONSTANT_LENGTH) > size) { + ui_expert(op, "Invalid file '%s' (bad version string length %d).", + filename, val); + goto done; + } + proc_version_string = nvalloc(val+1); + memcpy(proc_version_string, buf + offset, val); + offset += val; + proc_version_string[val] = '\0'; + + /* check if the running kernel matches */ + + if (strcmp(real_proc_version_string, proc_version_string) != 0) { + goto done; + } + + ui_log(op, "A precompiled kernel interface for kernel '%s' has been " + "found here: %s.", description, filename); + + /* extract kernel interface module */ + + len = size - offset; + + if ((dst_fd = open(output_filename, O_CREAT | O_RDWR | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) { + ui_error(op, "Unable to open output file '%s' (%s).", output_filename, + strerror(errno)); + goto done; + } + + /* set the output file length */ + + if ((lseek(dst_fd, len - 1, SEEK_SET) == -1) || + (write(dst_fd, "", 1) == -1)) { + ui_error(op, "Unable to set output file '%s' length %d (%s).\n", + output_filename, len, strerror(errno)); + goto done; + } + + /* mmap the dst */ + + dst = mmap(0, len, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, dst_fd, 0); + if (dst == (void *) -1) { + ui_error(op, "Unable to mmap output file %s (%s).\n", + output_filename, strerror(errno)); + goto done; + } + + /* copy */ + + memcpy(dst, buf + offset, len); + + /* + * now that we can no longer fail, allocate and initialize the + * PrecompiledInfo structure + */ + + info = (PrecompiledInfo *) nvalloc(sizeof(PrecompiledInfo)); + info->crc = crc; + info->major = major; + info->minor = minor; + info->patch = patch; + info->proc_version_string = proc_version_string; + info->description = description; + + /* + * XXX so that the proc version and description strings aren't + * freed below + */ + + proc_version_string = description = NULL; + + done: + + /* cleanup whatever needs cleaning up */ + + if (dst) munmap(dst, len); + if (buf) munmap(buf, size); + if (fd > 0) close(fd); + if (dst_fd > 0) close(dst_fd); + if (description) free(description); + if (proc_version_string) free(proc_version_string); + + return info; + +} /* mkprecompiled_unpack() */ diff --git a/precompiled.h b/precompiled.h new file mode 100644 index 0000000..38bac24 --- /dev/null +++ b/precompiled.h @@ -0,0 +1,56 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * precompiled.h + */ + +#ifndef __NVIDIA_INSTALLER_PRECOMPILED_H__ +#define __NVIDIA_INSTALLER_PRECOMPILED_H__ + +#define OUTPUT_FILENAME "nv-linux.o" + +typedef struct { + + uint32 crc; + uint32 major; + uint32 minor; + uint32 patch; + char *proc_version_string; + char *description; + +} PrecompiledInfo; + + +char *read_proc_version(Options *op); + +PrecompiledInfo *precompiled_unpack(Options *op, + const char *filename, + const char *output_filename, + const char *real_proc_version_string, + const int package_major, + const int package_minor, + const int package_patch); + + +#endif /* __NVIDIA_INSTALLER_PRECOMPILED_H__ */ diff --git a/sanity.c b/sanity.c new file mode 100644 index 0000000..c5527d3 --- /dev/null +++ b/sanity.c @@ -0,0 +1,305 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * sanity.c + */ + +#include "nvidia-installer.h" +#include "command-list.h" +#include "user-interface.h" +#include "backup.h" +#include "misc.h" +#include "sanity.h" + +#include <sys/types.h> +#include <sys/shm.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + + +/* + * sanity() - perform sanity tests on an existing installation + */ + +int sanity(Options *op) +{ + char *descr; + int major, minor, patch; + + /* check that there's a driver installed at all */ + + descr = get_installed_driver_version_and_descr(op, &major, &minor, &patch); + + if (!descr) { + ui_error(op, "Unable to find any installed NVIDIA driver. The sanity " + "check feature is only intended to be used with an existing " + "NVIDIA driver installation."); + return FALSE; + } + + ui_message(op, "The currently installed driver is: '%s' " + "(version: %d.%d-%d). nvidia-installer will now check " + "that all installed files still exist.", + descr, major, minor, patch); + + /* check that all the files are still where we placed them */ + + if (!test_installed_files(op)) { + ui_message(op, "The '%s' installation has been altered " + "since it was originally installed. It is recommended " + "that you reinstall.", descr); + return FALSE; + } + + /* check that shared memory works */ + + if (!check_sysvipc(op)) return FALSE; + + /* + * XXX There are lots of additional tests that could be added: + * + * - check for any conflicting libraries + * + * - check that the permissions on the /dev/nvidia* files haven't + * been screwed up by pam + * + * - check that /dev/zero has appropriate permissions + * + * - check for possible kernel config problems (IPC, mtrr support, + * etc). + */ + + ui_message(op, "'%s' (version: %d.%d-%d) appears to be installed " + "correctly.", descr, major, minor, patch); + + nvfree(descr); + + return TRUE; + +} /* sanity() */ + + +/* + * check_sysvipc() - test that shmat() and friends work + */ + +int check_sysvipc(Options *op) +{ + int shmid = -1; + int ret = FALSE; + int size = sysconf(_SC_PAGESIZE); + void *address = (void *) -1; + + shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777); + if (shmid == -1) goto done; + + address = shmat(shmid, 0, 0); + if (address == (void *) -1) goto done; + + ret = TRUE; + + done: + + if (shmid != -1) shmctl(shmid, IPC_RMID, 0); + if (address != (void *) -1) shmdt(address); + + if (ret) { + ui_log(op, "Shared memory test passed."); + } else { + ui_message(op, "Shared memory test failed (%s): please check that " + "your kernel has CONFIG_SYSVIPC enabled.", strerror(errno)); + } + + return ret; + +} /* check_sysvipc() */ + + +#if 0 + + +/* + * XXX don't have time to finish implementing and testing this right + * now... + */ + + + + +/* + * find_conflicting_libraries() - + */ + +static int find_conflicting_libraries(Options *op) +{ + FileList *l; + struct stat stat_buf; + int i; + + l = (FileList *) nvalloc(sizeof(FileList)); + + /* search for possibly conflicting libraries */ + + find_conflicting_xfree86_libraries(DEFAULT_XFREE86_INSTALLATION_PREFIX, l); + + if (strcmp(DEFAULT_XFREE86_INSTALLATION_PREFIX, op->xfree86_prefix) != 0) + find_conflicting_xfree86_libraries(op->xfree86_prefix, l); + + find_conflicting_opengl_libraries(DEFAULT_OPENGL_INSTALLATION_PREFIX, l); + + if (strcmp(DEFAULT_OPENGL_INSTALLATION_PREFIX, op->opengl_prefix) != 0) + find_conflicting_opengl_libraries(op->opengl_prefix, l); + + /* condense the file list */ + + condense_file_list(l); + + /* for each file in the list, check if it's an NVIDIA file */ + + for (i = 0; i < l->num; i++) { + if (lstat(l->filename[i], &stat_buf) == -1) { + ui_error(op, "Unable to determine properties for file '%s' (%s).", + l->filename[i], strerror(errno)); + continue; + } + + if (S_ISREG(stat_buf.st_mode)) { + ret = is_nvidia_library(op, l->filename[i]); + } else if (S_ISLNK(stat_buf.st_mode)) { + ret = is_nvidia_symlink(op, l->filename[i]); + } + } + + + + + /* free the FileList */ + + for (i = 0; i < l->num; i++) free(l->filename[i]); + free(l->filename); + free(l); + +} /* find_conflicting_libraries() */ + + + +/* + * is_nvidia_library() - mmap the file and scan through it for the + * nvidia string. + */ + +static int is_nvidia_library(Options *op, const char *filename) +{ + int fd = -1, ret = FALSE; + struct stat stat_buf; + char *buf = (void *) -1, char *found; + + if ((fd = open(filename, O_RDONLY)) == -1) { + ui_error(op, "Unable to open '%s' for reading (%s)", + filename, strerror(errno)); + goto done: + } + + if (fstat(fd, &stat_buf) == -1) { + ui_error(op, "Unable to determine size of '%s' (%s)", + filename, strerror(errno)); + goto done; + } + + if ((buf = mmap(0, stat_buf.st_size, PROT_READ, + MAP_FILE | MAP_SHARED, fd, 0)) == (void *) -1) { + ui_error(op, "Unable to map file '%s' for reading (%s)", + filename, strerror(errno)); + goto done; + } + + found = strstr(buf, "nvidia id: "); + + if (found) ret = TRUE; + + done: + + if (buf != (void *) -1) munmap(buf, stat_buf.st_size); + if (fd != -1) close(fd); + + return ret; + +} /* is_nvidia_library() */ + + +/* + * is_nvidia_symlink() - returns TRUE if this symlink should be moved + * out of the way. Find the target of the symlink, and recursively + * call is_nvidia_symlink() if the target is a symlink, or call + * is_nvidia_library() if the target is a regular file. + * + * XXX do we need to do anything about cyclic links? + */ + +static int is_nvidia_symlink(Options *op, const char *filename) +{ + char *tmp, *tmp2, *dir, *target; + int ret = TRUE; + struct stat stat_buf; + + tmp = get_symlink_target(op, filename); + if (!tmp) return FALSE; + + /* + * prepend the basename of the file, unless the target is an + * abosolute path + */ + + if (tmp[0] != '/') { + tmp2 = nvstrdup(tmp); + dir = dirname(tmp2); + target = nvstrcat(dir, "/", tmp, NULL); + nvfree(tmp); + nvfree(tmp2); + } else { + target = tmp; + } + + + if (lstat(target, &stat_buf) == -1) { + ui_error(op, "Unable to determine properties for file '%s' (%s).", + target, strerror(errno)); + return TRUE; /* return TRUE so that we don't try to back it up */ + } + + if (S_ISREG(stat_buf.st_mode)) { + ret = is_nvidia_library(op, target); + } else if (S_ISLNK(stat_buf.st_mode)) { + ret = is_nvidia_symlink(op, target); + } + + nvfree(target); + + return ret; + +} /* is_nvidia_symlink() */ + + +#endif diff --git a/sanity.h b/sanity.h new file mode 100644 index 0000000..135ee5a --- /dev/null +++ b/sanity.h @@ -0,0 +1,34 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * sanity.h + */ + +#ifndef __NVIDIA_INSTALLER_SANITY_H__ +#define __NVIDIA_INSTALLER_SANITY_H__ + +int sanity(Options *); +int check_sysvipc(Options *op); + +#endif /* __NVIDIA_INSTALLER_SANITY_H__ */ diff --git a/snarf-ftp.c b/snarf-ftp.c new file mode 100644 index 0000000..23e35db --- /dev/null +++ b/snarf-ftp.c @@ -0,0 +1,415 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * snarf-ftp.c - this source file contains functions for downloading + * files via the ftp protocol. Based on snarf + * (http://www.xach.com/snarf/) by Zachary Beane <xach@xach.com>, + * released under the GPL. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include <stdarg.h> + +#include <netdb.h> + +#ifdef USE_SOCKS5 +#define SOCKS +#include <socks.h> +#endif + +#include <errno.h> + +#include "snarf-internal.h" +#include "user-interface.h" +#include "misc.h" + + + +static void close_quit(int sock) +{ + if (sock) { + write(sock, "QUIT\r\n", 6); + close(sock); + } +} /* close_quit() */ + + + +static void ftp_set_defaults(UrlResource *rsrc, Url *u) +{ + if (!u->port) u->port = 21; + if (!u->username) u->username = strdup("anonymous"); + if (!u->password) u->password = strdup("snarf@"); + +} /* ftp_set_defaults() */ + + + +static void send_control(int sock, char *string, ...) +{ + va_list args; + char *line = NULL; + char *newline; + char *s = NULL; + + line = nvstrdup(string); + + va_start(args, string); + s = va_arg(args, char *); + while (s) { + newline = nvstrcat(line, s, NULL); + nvfree(line); + line = newline; + s = va_arg(args, char *); + } + va_end(args); + + write(sock, line, strlen(line)); + nvfree(line); + +} /* send_control() */ + + + +static char *get_line(UrlResource *rsrc, int control) +{ + int bytes_read = 0; + char *end; + char buf[SNARF_BUFSIZE+1]; + + while ((bytes_read = read(control, buf, SNARF_BUFSIZE))) { + + if (buf[0] == '4' || buf[0] == '5') return NULL; + + /* in case there's a partial read */ + buf[bytes_read] = '\0'; + + if (buf[bytes_read - 1] == '\n') buf[bytes_read - 1] = '\0'; + + if (buf[bytes_read - 2] == '\r') buf[bytes_read - 2] = '\0'; + + ui_expert(rsrc->op, buf); + + if (isdigit(buf[0]) && buf[3] == ' ') { + return strdup(buf); + } + + /* skip to last line of possibly multiple line input */ + + if ((end = strrchr(buf, '\n'))) { + end++; + if (isdigit(end[0]) && end[3] == ' ') + return strdup(end); + } + } + + return NULL; + +} /* get_line() */ + + + +static int check_numeric(const char *numeric, const char *buf) +{ + return ((buf[0] == numeric[0]) && + (buf[1] == numeric[1]) && + (buf[2] == numeric[2])); + +} /* check_numeric() */ + + + +static int sock_init(Options *op, struct sockaddr_in *sa, int control) +{ + socklen_t i; + int sock; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + ui_error(op, "unable to open socket (%s)", strerror(errno)); + return(0); + } + + i = sizeof(*sa); + + getsockname(control, (struct sockaddr *)sa, &i); + sa->sin_port = 0 ; /* let system choose a port */ + if (bind (sock, (struct sockaddr *)sa, sizeof (*sa)) < 0) { + ui_error(op, "unable to bind socket (%s)", strerror(errno)); + return 0; + } + + return sock; + +} /* sock_init() */ + + + +static int get_passive_sock(UrlResource *rsrc, int control) +{ + unsigned char *addr; + struct sockaddr_in sa; + int sock; + int x; + char *line, *orig_line; + + send_control(control, "PASV\r\n", NULL); + + if( !((line = get_line(rsrc, control)) && + check_numeric("227", line)) ) { + nvfree(line); + return 0; + } + + orig_line = line; + + if (strlen(line) < 4) { + nvfree(line); + return 0; + } + + if (!(sock = sock_init(rsrc->op, &sa, control))) return -1; + + /* skip the numeric response */ + line += 4; + + /* then find the digits */ + + while (!(isdigit(*line))) line++; + + /* ugliness from snarf 1.x */ + + sa.sin_family = AF_INET; + addr = (unsigned char *)&sa.sin_addr; + + for(x = 0; x < 4; x++) { + addr[x] = atoi(line); + line = strchr(line,',') + 1; + } + + addr = (unsigned char *)&sa.sin_port ; + addr[0] = atoi(line); + line = strchr(line,',') + 1; + addr[1] = atoi(line); + + if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + nvfree(orig_line); + ui_error(rsrc->op, "unable to connect (%s)", strerror(errno)); + return -1; + } + + nvfree(orig_line); + return sock; + +} /* get_passive_sock() */ + + + +static int get_sock(UrlResource *rsrc, int control) +{ + struct sockaddr_in sa; + unsigned char *addr; + unsigned char *port; + char *line; + char port_string[SNARF_BUFSIZE]; + unsigned int sock; + socklen_t i; + + if (!(sock = sock_init(rsrc->op, &sa, control))) return 0; + + + if (listen(sock, 0) < 0) { + ui_error(rsrc->op, "unable to listen (%s)", strerror(errno)); + return 0; + } + + i = sizeof(sa); + + getsockname(sock, (struct sockaddr *)&sa, &i); + + addr = (unsigned char *)(&sa.sin_addr.s_addr); + port = (unsigned char *)(&sa.sin_port); + + sprintf(port_string, "PORT %d,%d,%d,%d,%d,%d\r\n", + addr[0], addr[1], addr[2], addr[3], + port[0],(unsigned char)port[1]); + + send_control(control, port_string, NULL); + + if (!((line = get_line(rsrc, control)) && check_numeric("200", line))) { + nvfree(line); + return 0; + } + nvfree(line); + + return sock; + +} /* get_sock() */ + + + +/* I'm going to go to hell for not doing proper cleanup. */ + +int ftp_transfer(UrlResource *rsrc) +{ + Url *u = NULL; + char *line = NULL; + int sock = 0; + int data_sock = 0; + int passive = 1; + int retval = 0; + + u = rsrc->url; + + /* + * first of all, if this is proxied, just pass it off to the + * http module, since that's how we support proxying. + */ + + rsrc->proxy = get_proxy("FTP_PROXY"); + + if (rsrc->proxy) { + return http_transfer(rsrc); + } + + ftp_set_defaults(rsrc, u); + + if (!(sock = tcp_connect(rsrc->op, u->host, u->port))) + return FALSE; + + if (!(line = get_line(rsrc, sock))) + return FALSE; + + if (!check_numeric("220", line)) { + ui_error(rsrc->op, "bad ftp server greeting: %s", line); + nvfree(line); + return FALSE; + } + + send_control(sock, "USER ", u->username, "\r\n", NULL); + + if (!(line = get_line(rsrc, sock))) return FALSE; + + /* do the password dance */ + if (!check_numeric("230", line)) { + if (!check_numeric("331", line)) { + nvfree(line); + ui_error(rsrc->op, "bad/unexpected response: %s", line); + return FALSE; + } else { + nvfree(line); + + send_control(sock, "PASS ", u->password, "\r\n", NULL); + + if (!((line = get_line(rsrc, sock)) && + check_numeric("230", line)) ) { + nvfree(line); + ui_error(rsrc->op, "login failed"); + return FALSE; + } + nvfree(line); + } + } + + /* set binmode */ + send_control(sock, "TYPE I\r\n", NULL); + + if (!(line = get_line(rsrc, sock))) return 0; + nvfree(line); + + if (u->path) { + send_control(sock, "CWD ", u->path, "\r\n", NULL); + + if (!((line = get_line(rsrc, sock)) && + check_numeric("250", line))) { + nvfree(line); + close_quit(sock); + return 0; + } + nvfree(line); + } + + /* finally, the good stuff */ + + /* get a socket for reading. try passive first. */ + + if ((data_sock = get_passive_sock(rsrc, sock)) == -1) { + return FALSE; + } + + if (!data_sock) { + if ((data_sock = get_sock(rsrc, sock)) < 1) + return 0; + else + passive = 0; + } + + if (u->file) { + send_control(sock, "SIZE ", u->file, "\r\n", NULL); + line = get_line(rsrc, sock); + if (line && check_numeric("213", line)) { + rsrc->outfile_size = atoi(line + 3); + } else { + rsrc->outfile_size = 0; + } + } + + if (u->file) + send_control(sock, "RETR ", u->file, "\r\n", NULL); + else + send_control(sock, "NLST\r\n", NULL); + + if (!((line = get_line(rsrc, sock)) && + (check_numeric("150", line) || check_numeric("125", line)))) { + nvfree(line); + close_quit(sock); + return 0; + } + + if (!passive) + data_sock = accept(data_sock, NULL, NULL); + + nvfree(line); + + retval = dump_data(rsrc, data_sock); + + line = get_line(rsrc, sock); /* 226 Transfer complete */ + nvfree(line); + send_control(sock, "QUIT\r\n", NULL); + line = get_line(rsrc, sock); /* 221 Goodbye */ + nvfree(line); + + close(sock); + close(data_sock); + return retval; + +} /* ftp_transfer() */ diff --git a/snarf-http.c b/snarf-http.c new file mode 100644 index 0000000..555c86e --- /dev/null +++ b/snarf-http.c @@ -0,0 +1,489 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * snarf-http.c - this source file contains functions for downloading + * files via HTTP. Based on snarf (http://www.xach.com/snarf/) by + * Zachary Beane <xach@xach.com>, released under the GPL. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <pwd.h> +#include <ctype.h> +#include <unistd.h> + +#include "snarf.h" +#include "snarf-internal.h" +#include "user-interface.h" +#include "misc.h" + + +extern int default_opts; + +int redirect_count = 0; +#define REDIRECT_MAX 10 + +typedef struct _HttpHeader HttpHeader; +typedef struct _HttpHeaderEntry HttpHeaderEntry; +typedef struct _List List; + +struct _HttpHeader { + List *header_list; +}; + +struct _HttpHeaderEntry { + char *key; + char *value; +}; + +struct _List { + void *data; + List *next; +}; + + + +static List *list_new(void) +{ + List *new_list; + + new_list = malloc(sizeof(List)); + + new_list->data = NULL; + new_list->next = NULL; + + return new_list; + +} /* list_new() */ + + + +static void list_append(List *l, void *data) +{ + if (l->data == NULL) { + l->data = data; + return; + } + + while (l->next) { + l = l->next; + } + + l->next = list_new(); + l->next->data = data; + l->next->next = NULL; + +} /* list_append() */ + + + +/* written by lauri alanko */ +static char *base64(char *bin, int len) +{ + char *buf= (char *)malloc((len+2)/3*4+1); + int i=0, j=0; + + char BASE64_END = '='; + char base64_table[64]= "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + while( j < len - 2 ) { + buf[i++]=base64_table[bin[j]>>2]; + buf[i++]=base64_table[((bin[j]&3)<<4)|(bin[j+1]>>4)]; + buf[i++]=base64_table[((bin[j+1]&15)<<2)|(bin[j+2]>>6)]; + buf[i++]=base64_table[bin[j+2]&63]; + j+=3; + } + + switch ( len - j ) { + case 1: + buf[i++] = base64_table[bin[j]>>2]; + buf[i++] = base64_table[(bin[j]&3)<<4]; + buf[i++] = BASE64_END; + buf[i++] = BASE64_END; + break; + case 2: + buf[i++] = base64_table[bin[j] >> 2]; + buf[i++] = base64_table[((bin[j] & 3) << 4) + | (bin[j + 1] >> 4)]; + buf[i++] = base64_table[(bin[j + 1] & 15) << 2]; + buf[i++] = BASE64_END; + break; + case 0: + break; + } + buf[i]='\0'; + return buf; + +} /* base64() */ + + + +static HttpHeader *make_http_header(char *r) +{ + HttpHeader *h = NULL; + HttpHeaderEntry *he = NULL; + char *s = NULL; + char *raw_header = NULL; + char *raw_header_head = NULL; + + raw_header = strdup(r); + /* Track this to free at the end */ + raw_header_head = raw_header; + + h = malloc(sizeof(HttpHeader)); + h->header_list = list_new(); + + /* Skip the first line: "HTTP/1.X NNN Comment\r?\n" */ + s = raw_header; + while (*s != '\0' && *s != '\r' && *s != '\n') s++; + + while (*s != '\0' && isspace(*s)) s++; + + raw_header = s; + + s = strstr(raw_header, ": "); + while (s) { + /* Set ':' to '\0' to terminate the key */ + *s++ = '\0'; + he = malloc(sizeof(HttpHeaderEntry)); + he->key = strdup(raw_header); + /* Make it lowercase so we can lookup case-insensitive */ + nvstrtolower(he->key); + + /* Now get the value */ + s++; + raw_header = s; + while (*s != '\0' && *s != '\r' && *s != '\n') { + s++; + } + *s++ = '\0'; + he->value = strdup(raw_header); + list_append(h->header_list, he); + + /* Go to the next line */ + while (*s != '\0' && isspace(*s)) { + s++; + } + raw_header = s; + s = strstr(raw_header, ": "); + } + + free(raw_header_head); + return h; + +} /* make_http_header() */ + + + +static void free_http_header(HttpHeader *h) +{ + List *l; + List *l1; + HttpHeaderEntry *he; + + if (h == NULL) return; + + l = h->header_list; + while (l && l->data) { + he = l->data; + free(he->key); + free(he->value); + free(l->data); + l = l->next; + } + + l = h->header_list; + while (l) { + l1 = l->next; + free(l); + l = l1; + } +} /* free_http_header() */ + + + +static char *get_header_value(char *key, HttpHeader *header) +{ + List *l = NULL; + HttpHeaderEntry *he = NULL; + + l = header->header_list; + + while (l && l->data) { + he = l->data; + if (strcmp(he->key, key) == 0) { + return strdup(he->value); + } + l = l->next; + } + + return NULL; + +} /* get_header_value() */ + + + +static char *get_raw_header(int fd) +{ + char *header = NULL; + char buf[SNARF_BUFSIZE]; /* this whole function is pathetic. please + rewrite it for me. */ + int bytes_read = 0; + int total_read = 0; + + header = strdup(""); + + buf[0] = buf[1] = buf[2] = '\0'; + + while( (bytes_read = read(fd, buf, 1)) ) { + total_read += bytes_read; + + header = nvstrcat(header, buf, NULL); + if( total_read > 1) { + if( strcmp(header + (total_read - 2), "\n\n") == 0 ) + break; + } + + if( total_read > 3 ) { + if( strcmp(header + (total_read - 4), "\r\n\r\n") + == 0 ) + break; + } + } + + return header; + +} /* get_raw_header() */ + + + +static char *get_request(UrlResource *rsrc) +{ + char *request = NULL; + char *auth = NULL; + Url *u; + + u = rsrc->url; + + request = nvstrcat("GET ", u->path, u->file, " HTTP/1.0\r\n", + "Host: ", u->host, "\r\n", NULL); + + if (u->username && u->password) { + auth = nvstrcat(u->username, ":", u->password, NULL); + auth = base64(auth, strlen(auth)); + request = nvstrcat(request, "Authorization: Basic ", + auth, "\r\n", NULL); + } + + if (rsrc->proxy_username && rsrc->proxy_password) { + auth = nvstrcat(rsrc->proxy_username, ":", + rsrc->proxy_password, NULL); + auth = base64(auth, strlen(auth)); + request = nvstrcat(request, "Proxy-Authorization: Basic ", + auth, "\r\n", NULL); + } + + request = nvstrcat(request, "User-Agent: ", PROGRAM_NAME, "/", + NVIDIA_INSTALLER_VERSION, NULL); + + /* This CRLF pair closes the User-Agent key-value set. */ + request = nvstrcat(request, "\r\n", NULL); + + /* If SNARF_HTTP_REFERER is set, spoof it. */ + if (getenv("SNARF_HTTP_REFERER")) { + request = nvstrcat(request, "Referer: ", + getenv("SNARF_HTTP_REFERER"), + "\r\n", NULL); + } + + request = nvstrcat(request, "\r\n", NULL); + + return request; + +} /* get_request() */ + + +int http_transfer(UrlResource *rsrc) +{ + Url *u = NULL; + Url *proxy_url = NULL; + Url *redir_u = NULL; + char *request = NULL; + char *raw_header = NULL; + HttpHeader *header = NULL; + char *len_string = NULL; + char *new_location = NULL; + char buf[SNARF_BUFSIZE]; + int sock = 0; + ssize_t bytes_read = 0; + int retval = FALSE; + int i; + + /* make sure we haven't recursed too much */ + + if (redirect_count > REDIRECT_MAX) { + ui_error(rsrc->op, "redirection max count exceeded " + "(looping redirect?)"); + redirect_count = 0; + return FALSE; + } + + /* make sure everything's initialized to something useful */ + u = rsrc->url; + + if (! *(u->host)) { + ui_error(rsrc->op, "no host specified"); + return FALSE; + } + + /* fill in proxyness */ + if (!rsrc->proxy) { + rsrc->proxy = get_proxy("HTTP_PROXY"); + } + + if (!u->path) u->path = strdup("/"); + + if (!u->file) u->file = strdup(""); /* funny looking */ + + if (!u->port) u->port = 80; + + /* send the request to either the proxy or the remote host */ + if (rsrc->proxy) { + proxy_url = url_new(); + url_init(proxy_url, rsrc->proxy); + + if (!proxy_url->port) proxy_url->port = 80; + + if (!proxy_url->host) { + ui_error(rsrc->op, "bad proxy `%s'", rsrc->proxy); + return FALSE; + } + + if (proxy_url->username) + rsrc->proxy_username = strdup(proxy_url->username); + + if (proxy_url->password) + rsrc->proxy_password = strdup(proxy_url->password); + + /* Prompt for proxy password if not specified */ + if (proxy_url->username && !proxy_url->password) { + proxy_url->password = ui_get_input(rsrc->op, NULL, + "Password for " + "proxy %s@%s", + proxy_url->username, + proxy_url->host); + } + + if (!(sock = tcp_connect(rsrc->op, proxy_url->host, proxy_url->port))) + return FALSE; + + u->path = strdup(""); + u->file = strdup(u->full_url); + request = get_request(rsrc); + + write(sock, request, strlen(request)); + + } else /* no proxy */ { + + if (!(sock = tcp_connect(rsrc->op, u->host, u->port))) return FALSE; + + request = get_request(rsrc); + write(sock, request, strlen(request)); + } + + /* check to see if it returned an HTTP 1.x response */ + memset(buf, '\0', 5); + + bytes_read = read(sock, buf, 8); + + if (bytes_read == 0) { + close(sock); + return FALSE; + } + + if (!(buf[0] == 'H' && buf[1] == 'T' + && buf[2] == 'T' && buf[3] == 'P')) { + write(rsrc->out_fd, buf, bytes_read); + } else { + /* skip the header */ + buf[bytes_read] = '\0'; + raw_header = get_raw_header(sock); + raw_header = nvstrcat(buf, raw_header, NULL); + header = make_http_header(raw_header); + + /* if in expert mode, write the raw_header to the log */ + + ui_expert(rsrc->op, raw_header); + + /* check for redirects */ + new_location = get_header_value("location", header); + + if (raw_header[9] == '3' && new_location) { + redir_u = url_new(); + + /* make sure we still send user/password along */ + redir_u->username = nvstrdup(u->username); + redir_u->password = nvstrdup(u->password); + + url_init(redir_u, new_location); + rsrc->url = redir_u; + redirect_count++; + retval = transfer(rsrc); + goto cleanup; + } + + if (raw_header[9] == '4' || raw_header[9] == '5') { + for(i=0; raw_header[i] && raw_header[i] != '\n'; i++); + raw_header[i] = '\0'; + + if (!(rsrc->flags & SNARF_FLAGS_DOWNLOAD_SILENT)) { + ui_error(rsrc->op, "HTTP error from server: %s", raw_header); + } + + retval = FALSE; + goto cleanup; + } + + len_string = get_header_value("content-length", header); + + if (len_string) + rsrc->outfile_size = (off_t )atoi(len_string); + + if (get_header_value("content-range", header)) + rsrc->outfile_size += rsrc->outfile_offset; + } + + retval = dump_data(rsrc, sock); + + cleanup: + free_http_header(header); + close(sock); + return retval; + +} /* http_transfer() */ diff --git a/snarf-internal.h b/snarf-internal.h new file mode 100644 index 0000000..9fa04fc --- /dev/null +++ b/snarf-internal.h @@ -0,0 +1,87 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * snarf-internal.h + */ + +#ifndef __NVIDIA_INSTALLER_SNARF_INTERNAL_H__ +#define __NVIDIA_INSTALLER_SNARF_INTERNAL_H__ + +#include "nvidia-installer.h" + +typedef struct _UrlResource UrlResource; +typedef struct _Url Url; + +struct _Url { + char *full_url; + int service_type; + char *username; + char *password; + char *host; + int port; + char *path; + char *file; +}; + +struct _UrlResource { + Url *url; + Options *op; + int out_fd; + uint32 flags; + char *proxy; + char *proxy_username; + char *proxy_password; + off_t outfile_size; + off_t outfile_offset; +}; + + +/* Service types */ +enum url_services { + SERVICE_HTTP = 1, + SERVICE_FTP +}; + + +#define SNARF_BUFSIZE (5*2048) + +/* snarf.c */ + +Url *url_new (void); +Url *url_init (Url *, const char *); +void url_destroy(Url *); +char *get_proxy (const char *); +int dump_data (UrlResource *, int); +int tcp_connect(Options *, char *, int); +int transfer(UrlResource *rsrc); + +/* snarf-ftp.c */ + +int ftp_transfer(UrlResource *); + +/* snarf-http.c */ + +int http_transfer(UrlResource *); + +#endif /* __NVIDIA_INSTALLER_SNARF_INTERNAL_H__ */ @@ -0,0 +1,536 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * snarf.c - this source file contains functions for downloading + * files. Based on snarf (http://www.xach.com/snarf/) by Zachary + * Beane <xach@xach.com>, released under the GPL. + */ + +#include <stdlib.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <ctype.h> + +#include "nvidia-installer.h" + +#include "snarf.h" +#include "snarf-internal.h" + +#include "user-interface.h" +#include "misc.h" + + +static const char *get_url_service_type(const char *string, Url *u); +static const char *get_url_username (const char *string, Url *u); +static const char *get_url_password (const char *string, Url *u); +static const char *get_url_hostname (const char *url, Url *u); +static const char *get_url_port (const char *url, Url *u); +static const char *get_url_path (const char *url, Url *u); +static const char *get_url_file (const char *string, Url *u); + +static UrlResource *url_resource_new(void); +static void url_resource_destroy(UrlResource *rsrc); + +static const char *local_hstrerror(int n); + +/* + * snarf() - main entry point + */ + +int snarf(Options *op, const char *url, int out_fd, uint32 flags) +{ + UrlResource *rsrc = NULL; + int ret; + + rsrc = url_resource_new(); + rsrc->url = url_new(); + rsrc->op = op; + + if (url_init(rsrc->url, url) == NULL) { + ui_error(op, "'%s' is not a valid URL.", url); + return FALSE; + } + + rsrc->out_fd = out_fd; + rsrc->flags = flags; + + ret = transfer(rsrc); + url_resource_destroy(rsrc); + + return ret; + +} /* snarf() */ + + + +Url *url_new(void) +{ + Url *new_url; + + new_url = nvalloc(sizeof(Url)); + + new_url->full_url = NULL; + new_url->service_type = 0; + new_url->username = NULL; + new_url->password = NULL; + new_url->host = NULL; + new_url->port = 0; + new_url->path = NULL; + new_url->file = NULL; + + return new_url; + +} /* url_new() */ + + + +Url *url_init(Url *u, const char *string) +{ + const char *sp = string; + + u->full_url = nvstrdup(string); + + if (!(sp = get_url_service_type(sp, u))) return NULL; + + /* + * only get username/password if they are not null, + * allows us to handle redirects properly + */ + + if (!u->username) + sp = get_url_username(sp, u); + if (!u->password) + sp = get_url_password(sp, u); + + sp = get_url_hostname(sp, u); + + if (!(u->host && *(u->host))) return NULL; + + sp = get_url_port(sp, u); + + sp = get_url_path(sp, u); + sp = get_url_file(sp, u); + + return u; + +} /* url_init() */ + + + +void url_destroy(Url *u) +{ + if (!u) return; + + nvfree(u->full_url); + nvfree(u->username); + nvfree(u->password); + nvfree(u->host); + nvfree(u->path); + nvfree(u->file); + +} /* url_destroy() */ + + + +char *get_proxy(const char *firstchoice) +{ + char *proxy; + char *help; + + if ((proxy = getenv(firstchoice))) return proxy; + + help = nvstrdup(firstchoice); + nvstrtolower(help); + proxy = getenv(help); + nvfree(help); + if (proxy) return proxy; + + if ((proxy = getenv("SNARF_PROXY"))) return proxy; + + if ((proxy = getenv("PROXY"))) return proxy; + + return NULL; + +} /* get_proxy() */ + + + +int tcp_connect(Options *op, char *remote_host, int port) +{ + struct hostent *host; + struct sockaddr_in sa; + int sock_fd; + + if ((host = (struct hostent *)gethostbyname(remote_host)) == NULL) { + ui_error(op, "Unable to connect to %s (%s)", + remote_host, local_hstrerror(h_errno)); + return FALSE; + } + + /* get the socket */ + if((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + ui_error(op, "Unable to create socket (%s)", strerror(errno)); + return FALSE; + } + + /* connect the socket, filling in the important stuff */ + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + memcpy(&sa.sin_addr, host->h_addr,host->h_length); + + if(connect(sock_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0){ + ui_error(op, "Unable to connect to remote host %s (%s)", + remote_host, strerror(errno)); + return FALSE; + } + + return sock_fd; + +} /* tcp_connect() */ + + + +int dump_data(UrlResource *rsrc, int sock) +{ + int bytes_read = 0; + int total_bytes_read = 0; + ssize_t written = 0; + char buf[SNARF_BUFSIZE]; + char *msg; + float percent; + + msg = nvstrcat("Downloading: ", rsrc->url->full_url, NULL); + + if (rsrc->flags & SNARF_FLAGS_STATUS_BAR) { + ui_status_begin(rsrc->op, msg, "Downloading"); + } + + while ((bytes_read = read(sock, buf, SNARF_BUFSIZE))) { + + if (rsrc->flags & SNARF_FLAGS_STATUS_BAR) { + total_bytes_read += bytes_read; + percent = (float) total_bytes_read / (float) rsrc->outfile_size; + ui_status_update(rsrc->op, percent, NULL); + } + + written = write(rsrc->out_fd, buf, bytes_read); + if (written == -1) { + ui_error(rsrc->op, "Error while writing output file (%s)", + strerror(errno)); + close(sock); + return FALSE; + } + } + + close(sock); + + if (rsrc->flags & SNARF_FLAGS_STATUS_BAR) { + ui_status_end(rsrc->op, "done."); + } + + free(msg); + + return 1; + +} /* dump_data() */ + + + +int transfer(UrlResource *rsrc) +{ + int ret = FALSE; + + switch (rsrc->url->service_type) { + case SERVICE_HTTP: + ret = http_transfer(rsrc); + break; + case SERVICE_FTP: + ret = ftp_transfer(rsrc); + break; + default: + ret = FALSE; + break; + } + + return ret; +} + + + +/*************************************************************************/ +/* static functions */ + +static const char *get_url_service_type(const char *string, Url *u) +{ + /* + * fixme: what if the string isn't at the beginning of the string? + */ + + if (strstr(string, "http://")) { + string += 7; + u->service_type = SERVICE_HTTP; + return string; + } + + if (strstr(string, "ftp://")) { + string += 6; + u->service_type = SERVICE_FTP; + return string; + } + + if (strncasecmp(string, "www", 3) == 0) { + u->service_type = SERVICE_HTTP; + u->full_url = nvstrcat("http://", u->full_url, NULL); + return string; + } + + if (strncasecmp(string, "ftp", 3) == 0) { + u->service_type = SERVICE_FTP; + u->full_url = nvstrcat("ftp://", u->full_url, NULL); + return string; + } + + /* default to browser-style serviceless http URL */ + u->full_url = nvstrcat("http://", u->full_url, NULL); + u->service_type = SERVICE_HTTP; + return string; + +} /* get_url_service_type() */ + + + +static const char *get_url_username(const char *string, Url *u) +{ + int i; + char *username; + char *at; + char *slash; + + at = strchr(string, '@'); + slash = strchr(string, '/'); + + if ((!at) || (slash && (at >= slash))) return string; + + for (i = 0; string[i] && string[i] != ':' && string[i] != '@' && + string[i] != '/'; i++); + + if (string[i] != '@' && string[i] != ':') return string; + + username = nvalloc(i); + memcpy(username, string, i + 1); + + username[i] = '\0'; + + string += i + 1; + + u->username = username; + return string; + +} /* get_url_username() */ + + + +static const char *get_url_password(const char *string, Url *u) +{ + int i; + char *password; + char *at; + char *slash; + + at = strchr(string, '@'); + slash = strchr(string, '/'); + + if ((!at) || (slash && (at >= slash))) return string; + + /* + * skipping to the end of the host portion. this is kinda messy + * for the (rare) cases where someone has a slash and/or at in + * their password. It's not perfect; but it catches easy cases. + * + * If you know of a better way to do this, be my guest. I do not + * feel a particular paternal instinct towards my ugly code. + * + * I guess that applies to this whole program. + */ + + for (i = 0 ; string[i] != '@'; i++); + + password = nvalloc(i); + + /* and finally, get the password portion */ + + memcpy(password, string, i); + password[i] = '\0'; + + string += i + 1; + + u->password = password; + + return string; + +} /* get_url_password() */ + + + +static const char *get_url_hostname(const char *url, Url *u) +{ + char *hostname; + int i; + + /* skip to end, slash, or port colon */ + for (i = 0; url[i] && url[i] != '/' && url[i] != ':'; i++); + + hostname = nvalloc(i + 1); + + memcpy(hostname, url, i); + + hostname[i] = '\0'; + + /* if there's a port */ + if(url[i] == ':') + url += i + 1; + else + url += i; + + u->host = hostname; + return url; + +} /* get_url_hostname() */ + + + +static const char *get_url_port(const char *url, Url *u) +{ + char *port_string; + int i; + + for (i = 0; isdigit(url[i]); i++); + + if (i == 0) return url; + + port_string = nvalloc(i + 1); + memcpy(port_string, url, i + 1); + + port_string[i] = '\0'; + + url += i; + + u->port = atoi(port_string); + + return url; + +} /* get_url_port() */ + + + +static const char *get_url_path(const char *url, Url *u) +{ + int i; + char *path; + + /* find where the last slash is */ + for (i = strlen(url); i > 0 && url[i] != '/'; i--); + + if (url[i] != '/') return url; + + path = nvalloc(i + 2); + memcpy(path, url, i + 1); + path[i] = '/'; + path[i + 1] = '\0'; + + url += i + 1; + u->path = path; + + return url; + +} /* get_url_path() */ + + + +static const char *get_url_file(const char *string, Url *u) +{ + char *file; + + if (!string[0]) return NULL; + + file = nvalloc(strlen(string) + 1); + + memcpy(file, string, strlen(string) + 1); + + u->file = file; + + return string; + +} /* get_url_file() */ + + + +static UrlResource *url_resource_new(void) +{ + UrlResource *new_resource; + + new_resource = nvalloc(sizeof(UrlResource)); + + new_resource->url = NULL; + new_resource->out_fd = 0; + new_resource->proxy = NULL; + new_resource->proxy_username = NULL; + new_resource->proxy_password = NULL; + new_resource->op = NULL; + new_resource->outfile_size = 0; + new_resource->outfile_offset = 0; + + return new_resource; + +} /* url_resource_new() */ + + + +static void url_resource_destroy(UrlResource *rsrc) +{ + if (!rsrc) return; + + if(rsrc->url) url_destroy(rsrc->url); + + free(rsrc); + +} /* url_resource_destroy() */ + + + +static const char *local_hstrerror(int n) +{ + switch (n) { + case HOST_NOT_FOUND: return "unknown host"; + case NO_ADDRESS: return "no IP address associated with host"; + case NO_RECOVERY: return "fatal DNS error"; + case TRY_AGAIN: return "temporary DNS error (try again later)"; + default: return "unknown error"; + } +} /* local_hstrerror() */ @@ -0,0 +1,47 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * snarf.h + */ + +#ifndef __NVIDIA_INSTALLER_SNARF_H__ +#define __NVIDIA_INSTALLER_SNARF_H__ + +#include "nvidia-installer.h" + +/* + * SNARF_FLAGS_STATUS_BAR: when this flag is set, a status bar is + * displayed, showing the progress of the download. + */ +#define SNARF_FLAGS_STATUS_BAR 0x1 + +/* + * SNARF_FLAGS_DOWNLOAD_SILENT: when this flag is set, then snarf will + * not print error messages when the download fails + */ +#define SNARF_FLAGS_DOWNLOAD_SILENT 0x2 + +int snarf(Options *op, const char *url, int out_fd, uint32 flags); + +#endif /* __NVIDIA_INSTALLER_SNARF_H__ */ diff --git a/stream-ui.c b/stream-ui.c new file mode 100644 index 0000000..6e7806c --- /dev/null +++ b/stream-ui.c @@ -0,0 +1,467 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * stream_ui.c - implementation of the nvidia-installer ui using printf + * and friends. This user interface is compiled into nvidia-installer to + * ensure that we always have a ui available. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <signal.h> + +#include "nvidia-installer.h" +#include "nvidia-installer-ui.h" +#include "misc.h" +#include "files.h" +#include "format.h" + +/* prototypes of each of the stream ui functions */ + +int stream_detect (Options*); +int stream_init (Options*, FormatTextRows format_text_rows); +void stream_set_title (Options*, const char*); +char *stream_get_input (Options*, const char*, const char*); +int stream_display_license (Options*, const char*); +void stream_message (Options*, int level, const char*); +void stream_command_output (Options*, const char*); +int stream_approve_command_list(Options*, CommandList*, const char*); +int stream_yes_no (Options*, const int, const char*); +void stream_status_begin (Options*, const char*, const char*); +void stream_status_update (Options*, const float, const char*); +void stream_status_end (Options*, const char*); +void stream_close (Options*); + +InstallerUI stream_ui_dispatch_table = { + stream_detect, + stream_init, + stream_set_title, + stream_get_input, + stream_display_license, + stream_message, + stream_command_output, + stream_approve_command_list, + stream_yes_no, + stream_status_begin, + stream_status_update, + stream_status_end, + stream_close +}; + + +typedef struct { + int status_active; + char *status_label; +} Data; + + + +#define STATUS_BEGIN 0 +#define STATUS_UPDATE 1 +#define STATUS_END 2 + +#define STATUS_BAR_WIDTH 30 + +/* + * print_status_bar() - + */ + +static void print_status_bar(Data *d, int status, float percent) +{ + int i; + float val; + + if (status != STATUS_BEGIN) printf("\r"); + + if (d->status_label) { + printf(" %s: [", d->status_label); + } else { + printf(" ["); + } + + val = ((float) STATUS_BAR_WIDTH * percent); + + for (i = 0; i < STATUS_BAR_WIDTH; i++) { + printf("%c", ((float) i < val) ? '#' : ' '); + } + + printf("] %3d%%", (int) (percent * 100.0)); + + if (status == STATUS_END) printf("\n"); + + fflush(stdout); + +} /* print_status_bar() */ + + + +static void sigwinch_handler(int n) +{ + reset_current_terminal_width(0); +} + + +/* + * stream_detect - returns TRUE if the user interface is present; the + * stream_ui is always present, so this always returns TRUE. + */ + +int stream_detect(Options *op) +{ + return TRUE; + +} /* stream_detect() */ + + + +/* + * stream_init - initialize the ui and print a welcome message. + */ + +int stream_init(Options *op, FormatTextRows format_text_rows) +{ + Data *d = nvalloc(sizeof(Data)); + + d->status_active = FALSE; + op->ui_priv = d; + + if (!op->silent) { + + fmtout(""); + fmtout("Welcome to the NVIDIA Software Installer for Unix/Linux"); + fmtout(""); + + /* register the SIGWINCH signal handler */ + + signal(SIGWINCH, sigwinch_handler); + } + + return TRUE; + +} /* stream_init() */ + + + +/* + * stream_set_title() - other ui's might use this to update a welcome + * banner, window title, etc, but it doesn't make sense for this ui. + */ + +void stream_set_title(Options *op, const char *title) +{ + return; + +} /* stream_set_title() */ + + + +/* + * stream_get_input - prompt for user input with the given msg; + * returns the user inputed string. + */ + +char *stream_get_input(Options *op, const char *def, const char *msg) +{ + char *buf; + + format(stdout, NULL, ""); + format(stdout, NULL, msg); + fprintf(stdout, " [default: '%s']: ", def); + fflush(stdout); + + buf = fget_next_line(stdin, NULL); + + if (!buf || !buf[0]) { + if (buf) free(buf); + buf = nvstrdup(def); + } + + return buf; + +} /* stream_get_input() */ + + + + +/* + * stream_display_license() - print the text from the license file, + * prompt for acceptance from the user, and return whether or not the + * user accepted. + */ + +int stream_display_license(Options *op, const char *license) +{ + char *str; + + fmtout(""); + fmtout("Please read the following LICENSE and type \"" + "accept\" followed by the Enter key to accept the license, or " + "type anything else to not accept and exit nvidia-installer."); + fmtout(""); + + fmtout("________"); + fmtout(""); + printf("%s", license); + fmtout("________"); + fmtout(""); + + printf("Accept? (Type \"Accept\" to accept, or anything else to abort): "); + fflush(stdout); + + str = fget_next_line(stdin, NULL); + if (strlen(str) < 6) { + free(str); + return FALSE; + } + + str[7] = '\0'; + + if (strcasecmp(str, "ACCEPT") == 0) { + free(str); + return TRUE; + } else { + free(str); + return FALSE; + } + +} /* stream_display_license() */ + + + +/* + * stream_message() - print a message + */ + +void stream_message(Options *op, const int level, const char *msg) +{ + typedef struct { + char *prefix; + FILE *stream; + int newline; + } MessageLevelAttributes; + + const MessageLevelAttributes msg_attrs[] = { + { NULL, stdout, FALSE }, /* NV_MSG_LEVEL_LOG */ + { NULL, stdout, TRUE }, /* NV_MSG_LEVEL_MESSAGE */ + { "WARNING: ", stderr, TRUE }, /* NV_MSG_LEVEL_WARNING */ + { "ERROR: ", stderr, TRUE } /* NV_MSG_LEVEL_ERROR */ + }; + + Data *d = op->ui_priv; + + /* don't print log messages if we're currently displaying a status */ + + if ((level == NV_MSG_LEVEL_LOG) && (d->status_active)) return; + + if (msg_attrs[level].newline) format(msg_attrs[level].stream, NULL, ""); + format(msg_attrs[level].stream, msg_attrs[level].prefix, msg); + if (msg_attrs[level].newline) format(msg_attrs[level].stream, NULL, ""); + +} /* stream_message() */ + + + +/* + * stream_command_output() - if in expert mode, print output from + * executing a command. + * + * XXX Should this output be formatted? + */ + +void stream_command_output(Options *op, const char *msg) +{ + Data *d = op->ui_priv; + + if ((!op->expert) || (d->status_active)) return; + + fmtoutp(" ", msg); + +} /* stream_command_output() */ + + + +/* + * stream_approve_command_list() - list all the commands that will be + * executed, and ask for approval. + */ + +int stream_approve_command_list(Options *op, CommandList *cl, + const char *descr) +{ + int i; + Command *c; + char *perms; + const char *prefix = " --> "; + + fmtout(""); + fmtout("The following operations will be performed to install the %s:", + descr); + fmtout(""); + + for (i = 0; i < cl->num; i++) { + c = &cl->cmds[i]; + + switch (c->cmd) { + + case INSTALL_CMD: + perms = mode_to_permission_string(c->mode); + fmtoutp(prefix, "install the file '%s' as '%s' with " + "permissions '%s'", c->s0, c->s1, perms); + free(perms); + break; + + case RUN_CMD: + fmtoutp(prefix, "execute the command `%s`", c->s0); + break; + + case SYMLINK_CMD: + fmtoutp(prefix, "create a symbolic link '%s' to '%s'", + c->s0, c->s1); + break; + + case BACKUP_CMD: + fmtoutp(prefix, "back up the file '%s'", c->s0); + break; + + case DELETE_CMD: + fmtoutp(prefix, "delete file '%s'", c->s0); + break; + + default: + + fmterrp("ERROR: ", "Error in CommandList! (cmd: %d; s0: '%s';" + "s1: '%s'; mode: %04o)", c->cmd, c->s0, c->s1, c->mode); + fmterr("Aborting installation."); + return FALSE; + break; + } + } + + fflush(stdout); + + if (!stream_yes_no(op, TRUE, "\nIs this acceptable? (answering 'no' will " + "abort installation)")) { + fmterr("Command list not accepted; exiting installation."); + return FALSE; + } + + return TRUE; + +} /* stream_approve_command_list() */ + + + +/* + * stream_yes_no() - ask the yes/no question 'msg' and return TRUE for + * yes, and FALSE for no. + */ + +int stream_yes_no(Options *op, const int def, const char *msg) +{ + char *buf; + int eof, ret = def; + + format(stdout, NULL, ""); + format(stdout, NULL, msg); + if (def) fprintf(stdout, " [default: Yes]: "); + else fprintf(stdout, " [default: No]: "); + fflush(stdout); + + buf = fget_next_line(stdin, &eof); + + if (!buf) return FALSE; + + /* key off the first letter; otherwise, just use the default */ + + if (tolower(buf[0]) == 'y') ret = TRUE; + if (tolower(buf[0]) == 'n') ret = FALSE; + + free(buf); + + return ret; + +} /* stream_yes_no() */ + + + +/* + * stream_status_begin() - we assume that title is always passed, but + * sometimes msg will be NULL. + */ + +void stream_status_begin(Options *op, const char *title, const char *msg) +{ + Data *d = op->ui_priv; + + d->status_active = TRUE; + + fmtout(title); + d->status_label = nvstrdup(msg); + + print_status_bar(d, STATUS_BEGIN, 0.0); + +} /* stream_status_begin() */ + + + +/* + * stream_status_update() - + */ + +void stream_status_update(Options *op, const float percent, const char *msg) +{ + print_status_bar(op->ui_priv, STATUS_UPDATE, percent); + +} /* stream_status_update() */ + + + +/* + * stream_status_end() - + */ + +void stream_status_end(Options *op, const char *msg) +{ + Data *d = op->ui_priv; + + print_status_bar(op->ui_priv, STATUS_END, 1.0); + + nvfree(d->status_label); + d->status_active = FALSE; + +} /* stream_status_end() */ + + + +/* + * stream_close() - close the ui + */ + +void stream_close(Options *op) +{ + return; + +} /* stream_close() */ diff --git a/tls_test.c b/tls_test.c new file mode 100644 index 0000000..22dd131 --- /dev/null +++ b/tls_test.c @@ -0,0 +1,47 @@ +/* + * Trivial __thread variable test. + * + * Gareth Hughes <gareth@nvidia.com> + */ + +#include <signal.h> +#include <dlfcn.h> +#include <unistd.h> +#include <stdlib.h> + +void seghandle(int bar); + +int main(int argc, char *argv[]) +{ + void *handle; + int (*func)(void); + + signal(SIGSEGV, seghandle); + + if (argc != 2) { + exit(1); + } + + handle = dlopen(argv[1], RTLD_NOW); + if (!handle) { + exit(1); + } + + func = dlsym(handle, "getTLSVar"); + if (!func) { + exit(1); + } + + func(); + + if (dlclose(handle) != 0) { + exit(1); + } + + return 0; +} + +void seghandle(int bar) +{ + exit(1); +} diff --git a/tls_test_Linux-ia64 b/tls_test_Linux-ia64 Binary files differnew file mode 100755 index 0000000..23a50b7 --- /dev/null +++ b/tls_test_Linux-ia64 diff --git a/tls_test_Linux-x86 b/tls_test_Linux-x86 Binary files differnew file mode 100755 index 0000000..9622f7c --- /dev/null +++ b/tls_test_Linux-x86 diff --git a/tls_test_Linux-x86_64 b/tls_test_Linux-x86_64 Binary files differnew file mode 100755 index 0000000..4a7a927 --- /dev/null +++ b/tls_test_Linux-x86_64 diff --git a/tls_test_dso.c b/tls_test_dso.c new file mode 100644 index 0000000..e7f90b9 --- /dev/null +++ b/tls_test_dso.c @@ -0,0 +1,7 @@ +static __thread int foo; + +int getTLSVar(void) +{ + foo = 0; + return foo; +} diff --git a/tls_test_dso_Linux-ia64.so b/tls_test_dso_Linux-ia64.so Binary files differnew file mode 100755 index 0000000..b1ac50f --- /dev/null +++ b/tls_test_dso_Linux-ia64.so diff --git a/tls_test_dso_Linux-x86.so b/tls_test_dso_Linux-x86.so Binary files differnew file mode 100755 index 0000000..2418309 --- /dev/null +++ b/tls_test_dso_Linux-x86.so diff --git a/tls_test_dso_Linux-x86_64.so b/tls_test_dso_Linux-x86_64.so Binary files differnew file mode 100755 index 0000000..be74433 --- /dev/null +++ b/tls_test_dso_Linux-x86_64.so diff --git a/update.c b/update.c new file mode 100644 index 0000000..fd3e8aa --- /dev/null +++ b/update.c @@ -0,0 +1,303 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * update.c + */ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/mman.h> +#include <string.h> +#include <errno.h> + +#include "nvidia-installer.h" +#include "misc.h" +#include "snarf.h" +#include "backup.h" +#include "user-interface.h" +#include "update.h" + + +static char *get_latest_driver_version_and_filename(Options *op, + int *, int *, int *); + + + +/* + * update() - determine if there is a more recent driver available; if + * so, download it and install it. + */ + +int update(Options *op) +{ + char *descr, *filename, *tmpfile, *url, *cmd; + int x0, y0, z0, x1, y1, z1, ret, fd; + + descr = get_installed_driver_version_and_descr(op, &x0, &y0, &z0); + + filename = get_latest_driver_version_and_filename(op, &x1, &y1, &z1); + if (!filename) return FALSE; + + if (descr && !op->force_update) { + + /* + * if the currently installed driver version is the same as + * the latest, don't update. + */ + + if ((x0 == x1) && (y0 == y1) && (z0 == z1)) { + ui_message(op, "The latest %s (version %d.%d-%d) is already " + "installed.", descr, x0, y0, z0); + nvfree(descr); + return TRUE; + } + } + + /* build the temporary file and url strings */ + + tmpfile = nvstrcat(op->tmpdir, "/nv-update-XXXXXX", NULL); + url = nvstrcat(op->ftp_site, "/XFree86/", INSTALLER_OS, "-", + INSTALLER_ARCH, "/", filename, NULL); + nvfree(filename); + + /* create the temporary file */ + + if ((fd = mkstemp(tmpfile)) == -1) { + ui_error(op, "Unable to create temporary file (%s)", strerror(errno)); + return FALSE; + } + + /* download the file */ + + if (!snarf(op, url, fd, SNARF_FLAGS_STATUS_BAR)) { + ui_error(op, "Unable to download driver %s.", url); + return FALSE; + } + + close(fd); + + /* XXX once we setup gpg validate the binary here */ + + /* check the binary */ + + cmd = nvstrcat("sh ", tmpfile, " --check", NULL); + ret = run_command(op, cmd, NULL, FALSE, FALSE, TRUE); + nvfree(cmd); + + if (ret != 0) { + ui_error(op, "The downloaded file does not pass its integrety check."); + return FALSE; + } + + /* + * We're ready to execute the binary; first, close down the ui so + * that the new installer can take over. + */ + + ui_close(op); + + /* execute `sh <downloaded file> <arguments>` */ + + cmd = nvstrcat("sh ", tmpfile, " ", op->update_arguments, NULL); + ret = system(cmd); + nvfree(cmd); + + /* remove the downloaded file */ + + unlink(tmpfile); + + /* + * we've already shut down the ui, so no need to return from this + * function. + */ + + exit(ret); + + return TRUE; + +} /* update() */ + + + +/* + * report_latest_driver_version() - + */ + +int report_latest_driver_version(Options *op) +{ + char *descr, *filename, *url; + int x0, y0, z0, x1, y1, z1; + + descr = get_installed_driver_version_and_descr(op, &x0, &y0, &z0); + + filename = get_latest_driver_version_and_filename(op, &x1, &y1, &z1); + + if (!filename) { + nvfree(descr); + return FALSE; + } + + url = nvstrcat(op->ftp_site, "/XFree86/", INSTALLER_OS, "-", + INSTALLER_ARCH, "/", filename, NULL); + + if (descr) { + ui_message(op, "Currently installed version: %d.%d-%d; " + "latest available version: %d.%d-%d; latest driver " + "file: %s.", x0, y0, z0, x1, y1, z1, url); + nvfree(descr); + } else { + ui_message(op, "Latest version: %d.%d-%d; latest driver file: %s.", + x1, y1, z1, url); + } + + nvfree(filename); + nvfree(url); + + return TRUE; + +} /* report_latest_driver_version() */ + + + +/* + * append_update_arguments() - append the specified argument to the + * update_arguments string. + */ + +char *append_update_arguments(char *s, int c, const char *arg, + struct option l[]) +{ + char *t; + int i = 0; + + if (!s) s = nvstrcat(" ", NULL); + + /* + * don't place "--update" or "--force-update" in the update + * argument list (avoid infinite loop) + */ + + if ((c == 'u') || (c == 'f')) return s; + + do { + if (l[i].val == c) { + t = nvstrcat(s, " --", l[i].name, NULL); + nvfree(s); + s = t; + if (l[i].has_arg) { + t = nvstrcat(s, "=", arg, NULL); + nvfree(s); + s = t; + } + return (s); + } + } while (l[++i].name); + + return s; + +} /* append_update_arguments() */ + + + +/* + * get_latest_driver_version() - + */ + +static char *get_latest_driver_version_and_filename(Options *op, int *major, + int *minor, int *patch) +{ + int fd = -1; + char *tmpfile = NULL; + char *url = NULL; + char *str = (void *) -1; + char *s = NULL; + char *buf = NULL; + char *filename = NULL; + struct stat stat_buf; + + tmpfile = nvstrcat(op->tmpdir, "/nv-latest-XXXXXX", NULL); + url = nvstrcat(op->ftp_site, "/XFree86/", INSTALLER_OS, "-", + INSTALLER_ARCH, "/latest.txt", NULL); + + if ((fd = mkstemp(tmpfile)) == -1) { + ui_error(op, "Unable to create temporary file (%s)", strerror(errno)); + goto done; + } + + if (!snarf(op, url, fd, SNARF_FLAGS_DOWNLOAD_SILENT)) { + ui_error(op, "Unable to determine most recent NVIDIA %s-%s driver " + "version.", INSTALLER_OS, INSTALLER_ARCH); + goto done; + } + + if (fstat(fd, &stat_buf) == -1) { + ui_error(op, "Unable to determine most recent NVIDIA %s-%s driver " + "version (%s).", INSTALLER_OS, INSTALLER_ARCH, + strerror(errno)); + goto done; + } + + str = mmap(0, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0); + if (str == (void *) -1) { + ui_error(op, "Unable to determine most recent NVIDIA %s-%s driver " + "version (%s).", INSTALLER_OS, INSTALLER_ARCH, + strerror(errno)); + goto done; + } + + buf = get_next_line(str, NULL); + + if (!nvid_version(buf, major, minor, patch)) { + ui_error(op, "Unable to determine latest NVIDIA %s-%s driver " + "version (no version number found in %s)", url); + goto done; + } + + /* everything after the space is the filename */ + + s = strchr(buf, ' '); + if (!s) { + ui_error(op, "Unable to read filename from %s.", url); + goto done; + } + + s++; + filename = nvstrdup(s); + + done: + + if (buf) nvfree(buf); + if (str != (void *) -1) munmap(str, stat_buf.st_size); + if (fd != -1) close(fd); + + unlink(tmpfile); + + if (tmpfile) nvfree(tmpfile); + if (url) nvfree(url); + + return filename; + +} /* get_latest_driver_version() */ diff --git a/update.h b/update.h new file mode 100644 index 0000000..e2f5ee6 --- /dev/null +++ b/update.h @@ -0,0 +1,40 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * update.h + */ + +#ifndef __NVIDIA_INSTALLER_UPDATE_H__ +#define __NVIDIA_INSTALLER_UPDATE_H__ + +#include "nvidia-installer.h" + +#define _GNU_SOURCE /* XXX not portable */ +#include <getopt.h> + +int update(Options *); +int report_latest_driver_version(Options *); +char *append_update_arguments(char *s, int c, const char *arg, + struct option l[]); + +#endif /* __NVIDIA_INSTALLER_UPDATE_H__ */ diff --git a/user-interface.c b/user-interface.c new file mode 100644 index 0000000..1ef5a1c --- /dev/null +++ b/user-interface.c @@ -0,0 +1,642 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * user_interface.c - this source file contains an abstraction to the + * nvidia-installer user interface. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <string.h> +#include <dlfcn.h> +#include <errno.h> +#include <signal.h> +#include "nvidia-installer.h" +#include "nvidia-installer-ui.h" +#include "misc.h" +#include "files.h" + + +/* + * global user interface pointer + */ + +InstallerUI *__ui = NULL; + +/* + * filename of extracted user interface + */ + +char *__extracted_user_interface_filename = NULL; + +/* pull in the default stream_ui dispatch table from stream_ui.c */ + +extern InstallerUI stream_ui_dispatch_table; + +/* pull in the user interface data arrays and sizes */ + +extern const unsigned char ncurses_ui_array[]; +extern const int ncurses_ui_array_size; + +/* struct describing the ui data */ + +typedef struct { + char *descr; + char *filename; + const char *data_array; + const int data_array_size; +} user_interface_attribute_t; + + +static int extract_user_interface(Options *op, user_interface_attribute_t *ui); +static void ui_signal_handler(int n); + +/* + * ui_init() - initialize the user interface; we start by looping over + * each of the possible ui shared libs (gtk, ncurses) until we find + * one that will work; if neither will work, then fall back to the + * built-in stream ui. Once we have chosen our ui, call it's init() + * function and return TRUE. + */ + +int ui_init(Options *op) +{ + void *handle; + int i; + user_interface_attribute_t ui_list[] = { + /* { "nvidia-installer GTK+ user interface", NULL, NULL, 0 }, */ + { "nvidia-installer ncurses user interface", NULL, + ncurses_ui_array, ncurses_ui_array_size }, + { NULL, NULL, NULL, 0 } + }; + + /* dlopen() the appropriate ui shared lib */ + + __ui = NULL; + + i = 0; + + if (((op->ui_str) && (strcmp(op->ui_str, "none") == 0)) || (op->silent)) { + i = 1; + } + + for (; ui_list[i].descr && !__ui; i++) { + + if (!extract_user_interface(op, &ui_list[i])) continue; + + handle = dlopen(ui_list[i].filename, RTLD_NOW); + + if (handle) { + __ui = dlsym(handle, "ui_dispatch_table"); + if (__ui && __ui->detect(op)) { + log_printf(op, TRUE, NULL, "Using: %s", + ui_list[i].descr); + __extracted_user_interface_filename = ui_list[i].filename; + break; + } else { + log_printf(op, TRUE, NULL, "Unable to initialize: %s", + ui_list[i].descr); + dlclose(handle); + __ui = NULL; + } + } else { + log_printf(op, TRUE, NULL, "Unable to load: %s", + ui_list[i].descr); + log_printf(op, TRUE, NULL, ""); + } + } + + /* fall back to the always built-in stream ui */ + + if (!__ui) { + __ui = &stream_ui_dispatch_table; + log_printf(op, TRUE, NULL, "Using built-in stream user interface"); + } + + /* + * init the ui + * + * XXX if init() fails, we should try to fall back to the build-in + * stream ui. + */ + + if (!__ui->init(op, nv_format_text_rows)) return FALSE; + + /* handle some common signals */ + + signal(SIGHUP, ui_signal_handler); + signal(SIGALRM, ui_signal_handler); + signal(SIGABRT, ui_signal_handler); + signal(SIGSEGV, ui_signal_handler); + signal(SIGTERM, ui_signal_handler); + signal(SIGINT, ui_signal_handler); + signal(SIGILL, ui_signal_handler); + signal(SIGBUS, ui_signal_handler); + + /* so far, so good */ + + return TRUE; + +} /* init_ui () */ + + + +/* + * ui_set_title() - + */ + +void ui_set_title(Options *op, const char *fmt, ...) +{ + char *title; + va_list ap; + + if (op->silent) return; + + va_start(ap, fmt); + title = assemble_string(fmt, ap); + va_end(ap); + + __ui->set_title(op, title); + free(title); + +} /* ui_set_title() */ + + + +/* + * ui_get_input() - + */ + +char *ui_get_input(Options *op, const char *def, const char *fmt, ...) +{ + char *msg, *tmp = NULL, *ret; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (op->no_questions) { + ret = nvstrdup(def); + tmp = nvstrcat(msg, " (Answer: '", ret," ')", NULL); + if (!op->silent) { + __ui->message(op, NV_MSG_LEVEL_LOG, tmp); + } + } else { + ret = __ui->get_input(op, def, msg); + tmp = nvstrcat(msg, " (Answer: '", ret," ')", NULL); + } + log_printf(op, TRUE, NV_BULLET_STR, tmp); + nvfree(msg); + nvfree(tmp); + + return ret; + +} /* ui_get_input() */ + + + +/* + * ui_display_license() + */ + +int ui_display_license (Options *op, const char *license) +{ + if (op->silent) return TRUE; + + return __ui->display_license(op, license); + +} /* ui_display_license() */ + + + +/* + * ui_error() - have the ui display an error message + */ + +void ui_error(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + __ui->message(op, NV_MSG_LEVEL_ERROR, msg); + log_printf(op, TRUE, "ERROR: ", msg); + + free(msg); + +} /* ui_error() */ + + + +void ui_warn(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + __ui->message(op, NV_MSG_LEVEL_WARNING, msg); + log_printf(op, TRUE, "WARNING: ", msg); + + free(msg); + +} /* ui_error() */ + + + +void ui_message(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (!op->silent) __ui->message(op, NV_MSG_LEVEL_MESSAGE, msg); + + log_printf(op, TRUE, NV_BULLET_STR, msg); + + free(msg); + +} /* ui_message() */ + + +void ui_log(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (!op->silent) __ui->message(op, NV_MSG_LEVEL_LOG, msg); + log_printf(op, TRUE, NV_BULLET_STR, msg); + + free(msg); + +} /* ui_message() */ + + +/* + * ui_expert() - this is essentially the same as ui_log, but the ui is + * only called to display the message when we are in expert mode. + */ + +void ui_expert(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + if (!op->expert) return; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (!op->silent) __ui->message(op, NV_MSG_LEVEL_LOG, msg); + log_printf(op, FALSE, NV_BULLET_STR, msg); + + free (msg); + +} /* ui_expert() */ + + + +void ui_command_output(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (!op->silent) __ui->command_output(op, msg); + + log_printf(op, FALSE, NV_CMD_OUT_PREFIX, msg); + + free(msg); + +} /* ui_command_output() */ + + + +/* + * ui_approve_command_list() + */ + +int ui_approve_command_list(Options *op, CommandList *c, const char *fmt, ...) +{ + char *msg; + int ret; + va_list ap; + + if (!op->expert || op->no_questions) return TRUE; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + ret = __ui->approve_command_list(op, c, msg); + free(msg); + + if (ret) __ui->message(op, NV_MSG_LEVEL_LOG, "Commandlist approved."); + else __ui->message(op, NV_MSG_LEVEL_LOG, "Commandlist rejected."); + + return ret; + +} /* ui_approve_command_list() */ + + +/* + * ui_yes_no() + */ + +int ui_yes_no (Options *op, const int def, const char *fmt, ...) +{ + char *msg, *tmp = NULL; + int ret; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (op->no_questions) { + ret = def; + tmp = nvstrcat(msg, " (Answer: ", (ret ? "Yes" : "No"), ")", NULL); + if (!op->silent) { + __ui->message(op, NV_MSG_LEVEL_LOG, tmp); + } + } else { + ret = __ui->yes_no(op, def, msg); + tmp = nvstrcat(msg, " (Answer: ", (ret ? "Yes" : "No"), ")", NULL); + } + + log_printf(op, FALSE, NV_BULLET_STR, tmp); + nvfree(msg); + nvfree(tmp); + + return ret; + +} /* ui_yes_no() */ + + + +void ui_status_begin(Options *op, const char *title, const char *fmt, ...) +{ + char *msg; + va_list ap; + + log_printf(op, TRUE, NV_BULLET_STR, title); + + if (op->silent) return; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + __ui->status_begin(op, title, msg); + free(msg); +} + + + +void ui_status_update(Options *op, const float percent, const char *fmt, ...) +{ + char *msg; + va_list ap; + + if (op->silent) return; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + __ui->status_update(op, percent, msg); + free(msg); +} + + + +void ui_status_end(Options *op, const char *fmt, ...) +{ + char *msg; + va_list ap; + + va_start(ap, fmt); + msg = assemble_string(fmt, ap); + va_end(ap); + + if (!op->silent) __ui->status_end(op, msg); + log_printf(op, TRUE, NV_BULLET_STR, msg); + free(msg); +} + + + +void ui_close (Options *op) +{ + __ui->close(op); + + if (__extracted_user_interface_filename) { + unlink(__extracted_user_interface_filename); + } + + __ui = NULL; + +} /* ui_close() */ + + + +/* + * extract_user_interface() - we want the user interfaces to be shared + * libraries, separate from the main installer binary, to protect the + * main installer from any broken library dependencies that the user + * interfaces may introduce. However, we don't want to have to + * install the user interface shared libraries on the user's system + * (and risk not finding them -- or worse: finding the wrong ones -- + * when the installer gets run). + * + * The solution to this is to build the ui shared libraries as usual, + * but to include them INSIDE the installer binary as static data, so + * that the installer is contained within one single file. + * + * The user_interface_attribute_t struct contains everything that is + * necessary to extract the user interface files, dumping each to a + * temporary file so that it can be dlopen()ed. + */ + +static int extract_user_interface(Options *op, user_interface_attribute_t *ui) +{ + unsigned char *dst = (void *) -1; + int fd = -1; + + /* check that this ui is present in the binary */ + + if ((ui->data_array == NULL) || (ui->data_array_size == 0)) { + log_printf(op, TRUE, NULL, "%s: not present.", ui->descr); + return FALSE; + } + + /* create a temporary file */ + + ui->filename = nvstrcat(op->tmpdir, "/nv-XXXXXX", NULL); + + fd = mkstemp(ui->filename); + if (fd == -1) { + log_printf(op, TRUE, NULL, "unable to create temporary file (%s)", + strerror(errno)); + goto failed; + } + + /* set the temporary file's size */ + + if (lseek(fd, ui->data_array_size - 1, SEEK_SET) == -1) { + log_printf(op, TRUE, NULL, "Unable to set file size for '%s' (%s)", + ui->filename, strerror(errno)); + goto failed; + } + if (write(fd, "", 1) != 1) { + log_printf(op, TRUE, NULL, "Unable to write file size for '%s' (%s)", + ui->filename, strerror(errno)); + goto failed; + } + + /* mmap the temporary file */ + + if ((dst = mmap(0, ui->data_array_size, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, 0)) == (void *) -1) { + log_printf(op, TRUE, NULL, "Unable to map destination file '%s' " + "for copying (%s)", ui->filename, strerror(errno)); + goto failed; + } + + /* copy the data out to the file */ + + memcpy(dst, ui->data_array, ui->data_array_size); + + /* unmap the temporary file */ + + if (munmap(dst, ui->data_array_size) == -1) { + log_printf(op, TRUE, NULL, "Unable to unmap destination file '%s' " + "(%s)", ui->filename, strerror(errno)); + goto failed; + } + + /* close the file */ + + close(fd); + + return TRUE; + + failed: + + if (dst != (void *) -1) munmap(dst, ui->data_array_size); + if (fd != -1) { close(fd); unlink(ui->filename); } + if (ui->filename) free(ui->filename); + + return FALSE; + +} /* extract_user_interface() */ + + +/* + * ui_signal_handler() - if a signal goes off that is going to + * terminate the process, call the ui to close cleanly; then print a + * message to stderr. + */ + +static void ui_signal_handler(int n) +{ + const char *sig_names[] = { + "UNKNOWN", /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGBUS", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGUSR1", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGUSR2", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGSTKFLT", /* 16 */ + "SIGCHLD", /* 17 */ + "SIGCONT", /* 18 */ + "SIGSTOP", /* 19 */ + "SIGTSTP", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ + "SIGURG", /* 23 */ + "SIGXCPU", /* 24 */ + "SIGXFSZ", /* 25 */ + "SIGVTALRM", /* 26 */ + "SIGPROF", /* 27 */ + "SIGWINCH", /* 28 */ + "SIGIO", /* 29 */ + "SIGPWR", /* 30 */ + "SIGSYS", /* 31 */ + "SIGUNUSED", /* 31 */ + }; + + const char *s; + + if (__ui) __ui->close(NULL); /* + * XXX don't have an Options struct to + * pass to close() + */ + /* + * print to stderr with write(2) rather than fprintf(3), since + * fprintf isn't guaranteed to be reentrant + */ + + s = (n < 32) ? sig_names[n] : "UNKNOWN"; + + write(2, "Received signal ", 16); + write(2, s, strlen(s)); + write(2, "; aborting.\n", 12); + + exit(128 + n); + +} /* ui_signal_handler() */ diff --git a/user-interface.h b/user-interface.h new file mode 100644 index 0000000..b37fd06 --- /dev/null +++ b/user-interface.h @@ -0,0 +1,51 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2003 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * + * Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + * + * + * user_interface.h + */ + +#ifndef __NVIDIA_INSTALLER_USER_INTERFACE_H__ +#define __NVIDIA_INSTALLER_USER_INTERFACE_H__ + +#include "nvidia-installer.h" +#include "command-list.h" + +int ui_init (Options*); +void ui_set_title (Options*, const char*, ...); +char *ui_get_input (Options*, const char*, const char*, ...); +int ui_display_license (Options*, const char*); +void ui_error (Options*, const char*, ...); +void ui_warn (Options*, const char*, ...); +void ui_message (Options*, const char*, ...); +void ui_log (Options*, const char*, ...); +void ui_expert (Options*, const char*, ...); +void ui_command_output (Options*, const char*, ...); +int ui_approve_command_list(Options*, CommandList*,const char*, ...); +int ui_yes_no (Options*, const int, const char*, ...); +void ui_status_begin (Options*, const char*, const char*, ...); +void ui_status_update (Options*, const float, const char*, ...); +void ui_status_end (Options*, const char*, ...); +void ui_close (Options*); + +#endif /* __NVIDIA_INSTALLER_USER_INTERFACE_H__ */ |