summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Plattner <aplattner@nvidia.com>2008-02-13 10:20:36 -0800
committerAaron Plattner <aplattner@nvidia.com>2008-02-13 10:20:36 -0800
commit6d2a0069d419975ba01c2c423b18ef7cd2e76a6f (patch)
tree130e177fc9fa77d4c7d0788ba07c3b5af42dd84f
1.0-61061.0-6106
-rw-r--r--COPYING340
-rw-r--r--ChangeLog92
-rw-r--r--Makefile256
-rw-r--r--README106
-rw-r--r--backup.c1317
-rw-r--r--backup.h48
-rw-r--r--command-list.c714
-rw-r--r--command-list.h90
-rw-r--r--crc.c116
-rw-r--r--crc.h30
-rw-r--r--files.c1481
-rw-r--r--files.h61
-rw-r--r--format.c179
-rw-r--r--format.h43
-rw-r--r--gen-ui-array.c94
-rw-r--r--install-from-cwd.c653
-rw-r--r--kernel.c1568
-rw-r--r--kernel.h43
-rw-r--r--log.c168
-rw-r--r--misc.c1405
-rw-r--r--misc.h68
-rw-r--r--mkprecompiled.c744
-rw-r--r--ncurses-ui.c2139
-rw-r--r--nvidia-installer-ui.h155
-rw-r--r--nvidia-installer.c770
-rw-r--r--nvidia-installer.h321
-rw-r--r--precompiled.c341
-rw-r--r--precompiled.h56
-rw-r--r--sanity.c305
-rw-r--r--sanity.h34
-rw-r--r--snarf-ftp.c415
-rw-r--r--snarf-http.c489
-rw-r--r--snarf-internal.h87
-rw-r--r--snarf.c536
-rw-r--r--snarf.h47
-rw-r--r--stream-ui.c467
-rw-r--r--tls_test.c47
-rwxr-xr-xtls_test_Linux-ia64bin0 -> 14162 bytes
-rwxr-xr-xtls_test_Linux-x86bin0 -> 3220 bytes
-rwxr-xr-xtls_test_Linux-x86_64bin0 -> 5064 bytes
-rw-r--r--tls_test_dso.c7
-rwxr-xr-xtls_test_dso_Linux-ia64.sobin0 -> 10260 bytes
-rwxr-xr-xtls_test_dso_Linux-x86.sobin0 -> 3384 bytes
-rwxr-xr-xtls_test_dso_Linux-x86_64.sobin0 -> 4920 bytes
-rw-r--r--update.c303
-rw-r--r--update.h40
-rw-r--r--user-interface.c642
-rw-r--r--user-interface.h51
48 files changed, 16868 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -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)
diff --git a/README b/README
new file mode 100644
index 0000000..970569a
--- /dev/null
+++ b/README
@@ -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__ */
diff --git a/crc.c b/crc.c
new file mode 100644
index 0000000..d085e1a
--- /dev/null
+++ b/crc.c
@@ -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() */
diff --git a/crc.h b/crc.h
new file mode 100644
index 0000000..17fbb82
--- /dev/null
+++ b/crc.h
@@ -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__ */
diff --git a/files.c b/files.c
new file mode 100644
index 0000000..02934e0
--- /dev/null
+++ b/files.c
@@ -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() */
diff --git a/files.h b/files.h
new file mode 100644
index 0000000..67a2896
--- /dev/null
+++ b/files.h
@@ -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__ */
diff --git a/log.c b/log.c
new file mode 100644
index 0000000..84d8ef6
--- /dev/null
+++ b/log.c
@@ -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() */
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..dc8bfd6
--- /dev/null
+++ b/misc.c
@@ -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() */
diff --git a/misc.h b/misc.h
new file mode 100644
index 0000000..d182d13
--- /dev/null
+++ b/misc.h
@@ -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__ */
diff --git a/snarf.c b/snarf.c
new file mode 100644
index 0000000..2788e4f
--- /dev/null
+++ b/snarf.c
@@ -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() */
diff --git a/snarf.h b/snarf.h
new file mode 100644
index 0000000..75d894c
--- /dev/null
+++ b/snarf.h
@@ -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
new file mode 100755
index 0000000..23a50b7
--- /dev/null
+++ b/tls_test_Linux-ia64
Binary files differ
diff --git a/tls_test_Linux-x86 b/tls_test_Linux-x86
new file mode 100755
index 0000000..9622f7c
--- /dev/null
+++ b/tls_test_Linux-x86
Binary files differ
diff --git a/tls_test_Linux-x86_64 b/tls_test_Linux-x86_64
new file mode 100755
index 0000000..4a7a927
--- /dev/null
+++ b/tls_test_Linux-x86_64
Binary files differ
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
new file mode 100755
index 0000000..b1ac50f
--- /dev/null
+++ b/tls_test_dso_Linux-ia64.so
Binary files differ
diff --git a/tls_test_dso_Linux-x86.so b/tls_test_dso_Linux-x86.so
new file mode 100755
index 0000000..2418309
--- /dev/null
+++ b/tls_test_dso_Linux-x86.so
Binary files differ
diff --git a/tls_test_dso_Linux-x86_64.so b/tls_test_dso_Linux-x86_64.so
new file mode 100755
index 0000000..be74433
--- /dev/null
+++ b/tls_test_dso_Linux-x86_64.so
Binary files differ
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__ */